Detect sub figures and split (automatically?)

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):

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]


Followed by the export as animated GIF:

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


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


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


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


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


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

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


• Very nice edit! – C. E. Oct 24 '19 at 16:36

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]


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[

The padding is what it is because ImagePartition discards images that are smaller than the given size.
• 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? – Markus Roellig Oct 22 '19 at 7:37
• @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. – C. E. Oct 22 '19 at 10:15