Here I present another implementation based on the findings from this answer. It doesn't depend on ImageAlign
or LongestCommonSubsequencePositions
and uses direct linear search for finding the perfect alignment. With the default settings it is already sufficiently fast and robust, but one can try to decrease the search window for larger speed (with narrower search window some parasite additional alignments may appear, and in such situations algorithm automatically increases the window). One can also try to decrease the shrinking factor hStep
if the algorithm fails completely (the default hStep
subsamples the original images in the horizontal direction with targed width about 50 pixels, what may be too small in some situations). The algorithm requires the screenshots to be successive and of equal width, but allowed to have different heights.
Here is the implementation (one may comment out the Echo
that prints the diagnostic plot):
Clear[distance, extractAlignments, findAlignments, findCrop]
distance[id1_, id2_, align_, maxWindow_] :=
Module[{dists =
Abs[Flatten[
Normal@id1[[align ;; Max[1, align - maxWindow + 1] ;; -1]] -
Normal@id2[[;; Min[align, maxWindow]]]]]}, {align,
Max[Max[dists] - Min[dists], Total[TakeLargest[dists, 20]]/20.]}]
extractAlignments[distances_, thr_] :=
Module[{minimums =
Select[DeleteCases[
SplitBy[distances, Last[#] <= thr &], {{1, _}, ___}], #[[1, 2]] <= thr &]},
If[MemberQ[Length /@ minimums, _?(# > 1 &)], {}, minimums[[All, 1]]]];
findAlignments[id1_, id2_, initAlign_, maxWindow_, hStep_, thr_ : 55] :=
Module[{width, height, hstep, distances, alignments}, height = Dimensions[id1][[1]];
distances = Table[distance[id1, id2, align, maxWindow], {align, initAlign, height}];
alignments = extractAlignments[distances, thr];
Echo[ListLinePlot[distances, PlotRange -> All, ImageSize -> 550, AspectRatio -> 1/8,
GridLines -> {Prepend[
alignments[[All, 1]], {maxWindow,
Directive[{Darker@Yellow, AbsoluteThickness[2], AbsoluteDashing[3]}]}], {thr}},
PlotStyle -> Directive[{Blue, JoinForm["Round"]}],
GridLinesStyle -> {Directive[{Green, AbsoluteThickness[6]}],
Directive[{Red, Thin, AbsoluteDashing[1]}]},
PlotLabel ->
Style[<|"alignments" -> alignments[[All, 1]], "distances" -> alignments[[All, 2]],
"maxWindow" -> maxWindow, "hStep" -> hStep, "thr" -> thr|>, 10]]];
alignments[[All, 1]]];
findCrop::multiPeak =
"No confident alignment found. Trying again with doubled maximum window height `` \
pixels.";
findCrop::window =
"Maximal height of the window can't be larger than the heigth of the smallest image. \
The maximal window height is set to ``.";
findCrop::imageDims =
"The widths and numbers of channels of the screenshots must be equal. Aborting.";
findCrop[i1_, i2_, initAlign_ : 20, maxWindow_ : 400, hStep_ : Automatic, thr_ : 55] :=
Module[{width, height, hstep, maxwindow, id1, id2, distances, alignments = {}},
{width[1], height[1]} = ImageDimensions[i1];
{width[2], height[2]} = ImageDimensions[i2];
hstep = If[hStep === Automatic, Quotient[width[1], 50], hStep];
id1 = Image`InternalImageData[i1, Interleaving -> True,
DataReversed -> True][[All, ;; ;; hstep]];
id2 = Image`InternalImageData[i2, Interleaving -> True][[All, ;; ;; hstep]];
If[Most@Dimensions[id1] =!= Most@Dimensions[id2], Message[findCrop::imageDims];
Abort[]];
If[TrueQ[maxWindow > Min[height[1], height[2]]], maxwindow = Min[height[1], height[2]];
Message[findCrop::window, maxwindow], maxwindow = maxWindow];
While[Length[alignments] != 1,
alignments = findAlignments[id1, id2, initAlign, maxwindow, hstep, thr];
If[Length[alignments] != 1, maxwindow = Min[2 maxwindow, height[1], height[2]];
Message[findCrop::multiPeak, maxwindow]];];
height[1] - alignments[[1]]];
Here is how it can be used:
First, load a testing set of screenshots:
imgs = Import /@ {"https://i.imgur.com/sXrjTom.jpg", "https://i.imgur.com/gWvFAod.jpg",
"https://i.imgur.com/achK7u2.jpg", "https://i.imgur.com/4SWtQID.jpg",
"https://i.imgur.com/Sbm3AZS.jpg", "https://i.imgur.com/87qCEqW.jpg",
"https://i.imgur.com/nOOvWvJ.jpg", "https://i.imgur.com/t74mRht.jpg",
"https://i.imgur.com/LY2tYa5.jpg", "https://i.imgur.com/bwZDNQz.jpg",
"https://i.imgur.com/LUMa0YU.jpg", "https://i.imgur.com/i2gqr5B.jpg",
"https://i.imgur.com/WIxrUGt.jpg", "https://i.imgur.com/QuhX70R.jpg",
"https://i.imgur.com/jfCTehS.jpg", "https://i.imgur.com/eoULwVr.jpg",
"https://i.imgur.com/BR4prMJ.jpg"};
Crop them:
imgsCrop = ImageTake[#, {230, 2210}] & /@ imgs;
Find the cropping heights for the screenshots (I intentionally set here too small value for maximum window height just to demonstrate how the algorithm behaves in ambiguous situations):
crops = Table[findCrop[imgsCrop[[i]], imgsCrop[[i + 1]], 1, 100], {i, Length[imgsCrop] - 1}];

{1368, 1400, 1419, 1547, 1334, 1324, 1531, 1524, 1433, 1571, 1608, 1525, 1333, 1485, 1541, 499}
Now assemble the final image and export it:
finalImage =
ImageAssemble@
Append[Table[{ImageTake[imgsCrop[[i]], crops[[i]]]}, {i,
Length[imgsCrop] - 1}], {imgsCrop[[-1]]}];
Export["Final Image.png", finalImage]
image2
,image3
etc before attempting anything automatic. $\endgroup$image2
、image3
can be different picture completely.I just use SE's screenshots to express this kind of question in here.So If you wanna crop it manually,It is a very dull works.And your case of that link have no part which is juxtaposition each other. $\endgroup$ImageStitch
. $\endgroup$