7
$\begingroup$

I would like to take a large figure showing a sequence of individual plots and automatically split the into series of images showing the individual sub-plots (to be exported as animated GIF later). So far I do it manually. The large image is cut from a PDF (called img):

enter image description here

I manually crop the image:

img = ImageTake[img, {10, 837}, {2, 1049}]

id = ImageDimensions[img]
(* {1048, 828} *)

When I partition the image I miss the frames on some sides because they are only 1 pixel wide.

imgs = Partition[Flatten[ImagePartition[img, Round[id/4]]], 1]

enter image description here

Followed by the export as animated GIF:

Export[<insert file name here>, imgs, "GIF","AnimationRepetitions" -> Infinity]

enter image description here

I would be grateful for any input that can automate the process and improve the quality of the final frames.

EDIT

Following up on the help by C.E. here is my improved approached. I also used some input from this question/answers. It is not automatic yet, but I got sufficiently nice results.

Starting from the aligned and padded pieces

images = Flatten@ImagePartition[
ImagePad[img, {{10, 15}, {15, 10}}, White],
{xdim/4 + 20, ydim/4 + 20},
{xdim/4, ydim/4}
];
aligned =   ImageAlign[images, 
      Method -> "FourierBlurInvariant", 
      TransformationClass -> "Translation"];

Note, that I added the second argument White to ´ImagePad` to prevent the default padding with black pixel. I then start by visually checking the correct identification of the vertical frame lines

HighlightImage[#, 
 ImageLines[
   MorphologicalBinarize[
     GaussianFilter[#, 3, Switch[\[Pi]/2, 0, {2, 0}, Pi/2, {0, 2}]], 
     {0.02, 0.05}], 
   0.4]& /@ aligned

enter image description here

The parameter 0.4 in ImageLines can be used to tune the identification. Now I take the (common) column number of the two vertical lines:

vlines = Sort[{Min[#[[1]]], 
 Max[#[[2]]]}] & /@ ((
  ImageLines[
    MorphologicalBinarize[
     GaussianFilter[#, 3, Switch[\[Pi]/2, 0, {2, 0}, Pi/2, {0, 2}]],
     {0.02, 0.05}], 
   0.4][[All, 1, All, 1]] & /@ aligned)

 (* {{17.1989, 278.839}, {17.1989, 277.836}, {17.0842, 277.951}, {18.0871,    277.951}, {17.0842, 277.951}, {17.1989, 277.836}, {17.0842, 277.951}, {17.1989, 277.836}, {17.0842, 277.951}, {17.1989, 277.836}, {17.0842, 277.951}, {17.1989, 277.836}, {18.0871, 277.951}, {17.1989, 277.836}, {17.0842, 277.951}, {17.1989,   277.836}} *) 

17 and 278 seems to be the common vertical position. Manually padding a little for sufficient image quality I get

 vcropped = ImageTake[#, {1, -1}, {16, 277}] & /@ aligned

enter image description here

As we see, all vertical frame axes are conserved and aligned. The same approach for the horizontal axes

 hlines = Sort[{
    Min[#[[1]]], 
    Max[#[[2]]]}] & /@ ((ImageLines[
    MorphologicalBinarize[
     GaussianFilter[#, 3, 
      Switch[0, 0, {2, 0}, Pi/2, {0, 2}]], {0.05, 0.02}], 0.4])[[
    All, 1, All, 2]] & /@ vcropped)

 (* {{10.3036, 215.695}, {10.3036, 215.695}, {10.3036, 215.695}, {10.3036,   215.695}, {10.3036, 216.696}, {10.3036, 216.696}, {10.3036,   216.696}, {10.3036, 216.696}, {11.3055, 216.696}, {11.3055,   216.696}, {11.3055, 216.696}, {11.3055, 216.696}, {10.3036,   216.696}, {10.3036, 216.696}, {10.3036, 216.696}, {10.3036,   216.696}}*)

 HighlightImage[#, 
  ImageLines[
    MorphologicalBinarize[
      GaussianFilter[#, 3, Switch[0, 0, {2, 0}, Pi/2, {0, 2}]], {0.05, 
    0.02}], 
   0.4]] & /@ vcropped

enter image description here

 hcropped = ImageTake[#, {11, 217}, {1, -1}] & /@ vcropped

enter image description here

The frames are properly conserved. We realign everthing and the result is ok.

 alignedCropped = ImageAlign[hcropped, TransformationClass -> "Translation"];

enter image description here

$\endgroup$
1
  • 1
    $\begingroup$ Very nice edit! $\endgroup$
    – C. E.
    Oct 24, 2019 at 16:36

1 Answer 1

7
$\begingroup$

ImageAlign should probably be at least part of the solution:

img = Import["https://i.stack.imgur.com/pc6ul.png"];
{xdim, ydim} = ImageDimensions[img];
images = Flatten@ImagePartition[img, {xdim/4, ydim/4}];
aligned = ImageAlign[images];
ListAnimate[aligned]

Alignment demo

Here is an attempt to make it more robust and to prevent it from clipping parts of the figure off at the edges:

images = Flatten@ImagePartition[
    ImagePad[img, {{10, 15}, {15, 10}}],
    {xdim/4 + 20, ydim/4 + 20},
    {xdim/4, ydim/4}
    ];
aligned = FoldList[ImageAlign, images];
ListAnimate[aligned]

Alignment demo 2

The padding is what it is because ImagePartition discards images that are smaller than the given size.

$\endgroup$
2
  • $\begingroup$ Thanks for the reply. ImageAlign is helping. Is there a way to detect the frame in the individual plots and crop everything that is outside? $\endgroup$ Oct 22, 2019 at 7:37
  • $\begingroup$ @MarkusRoellig I'm sure there is, but it's hard to know how well strategies for that generalize without having more examples. If you're doing this manually, I might at least note that one of the advantages of ImageAlign is that the coordinate system is the same in all the individual plots, so you only need to find the cropping values for one, that will work perfectly for all the other ones as well. $\endgroup$
    – C. E.
    Oct 22, 2019 at 10:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.