I have a bunch of small images that I need to register with larger images with sub-pixel resolution (ie. based on image interpolation )
This works.. seeking advice to improve performance.
Create a sample image which is offset by a fraction of a pixel from a larger image:
i0 = ColorConvert[ExampleData[{"TestImage", "Lena"}], "GrayScale"];
shift = {-0.2, 0.3};
w = 100;
h = 200;
x0 = {200, 300}
small = ImageTake[ImageTransformation[i0, (# + shift) &, DataRange -> Full],
x0[[1]] + {1, h}, x0[[2]] + {1, w}];
ImageAlign
nicely (quickly) locates the sub image in the host to within a pixel:
foundregion = Transpose[{First@#, Last@# } &@Position[ Map[Norm,
ImageData[ImageAlign[i0, small]], {2}],
x_ /; x > 0, {2}]] + {{1, 0}, {0, -1}} ;
nadd = 3;
localmatch = ImageTake[i0, Sequence @@ ( foundregion + nadd {{-1, 1}, {-1, 1}})];
now we have two small images, the left is 3 pix larger to avoid edge effects when we do interpolation.
GraphicsRow[ {localmatch , small }]
here is where the time issue is, brute force image difference..
err[x_?NumericQ, y_?NumericQ] :=
Flatten[(ImageData[
ImageSubtract[ ImageTake[ ImageTransformation[localmatch,
(# + {x, y}) & , DataRange -> Full] , nadd + {1, h},
nadd + {1, w}] , small ]]), 1] // Norm;
(r = NMinimize[ err[x, y], {{x, -2, 2}, {y, -2, 2}}] ) // Timing
{26.722971, {0., {x -> 0.800107, y -> 0.300113}}}
{ ({x, y} /. Last@r ) - foundregion[[All, 1]] + x0 , shift }
verify we recover the input:
{{-0.199893, 0.300113}, {-0.2, 0.3}}
Any thoughts on speeding it up?
edit
After making @nikie's changes (dramitcally faster) and playing with it a bit I found the way I was extracting the region of the aligned image was not reliable. this does the job well:
foundregion = #[[Ordering[#][[{1, -1}]]]] & /@
(Transpose@Position[ Map[Norm,
ImageData[ImageAlign[i0, small]], {2}], x_ /; x > 0, {2}]);
However this is now my bottleneck. Frustrating, ImageAlign
itself is very fast, but there seems to be no simple way to retrieve the actual displacement it has computed.
(# + {x, y}) &
withTranslationTransform[{x, y}]
- that makes it about 10x faster on my PC (I'm guessingImageTransformation
is optimized forTransformationFunction
s). ReplacingNMinimize
withFindMinimum
makes it 10x faster, again. You can probably make it faster if you carefully think about howImageTransformation
interpolates between pixels (there might even be a closed form solution?), but that's a lot more work for 0.2 seconds. $\endgroup$ – Niki Estner Oct 25 '14 at 9:21FindMinimum
produces a slightly less accurate result for some reason, but for present purpose that's not an issue. $\endgroup$ – george2079 Oct 27 '14 at 14:56FindMinimum
is of course only looking for a local minimum, not global. That might be the reason. $\endgroup$ – dr.blochwave Oct 27 '14 at 18:36ImageAlign
, use the secret optionImageAlign[i0, small, "FindGeometricTransformOutputQ" -> True]
$\endgroup$ – Simon Woods Oct 27 '14 at 21:48