18
$\begingroup$

I have two images from motor scan, Image1 is shifted by MxN pixels from Image2. How can I find the shift between Image1 and Image2 ?

Image -1 1 Image -2 2

If we compare the shift between the images in terms of {X,Y} I will be able to tell how many steps of the motor scan corresponds to the pixel shift on the image array sensor.

$\endgroup$
2
  • $\begingroup$ en.wikipedia.org/wiki/Phase_correlation - dont know if there is a buitin mathematica procedure though or if you need to roll your own. $\endgroup$
    – george2079
    Nov 1, 2013 at 18:27
  • $\begingroup$ All answers are equally good. It wouldn't be right if I choose one of them as the right answer. $\endgroup$ Nov 2, 2013 at 7:04

4 Answers 4

9
$\begingroup$

There are many different image registration algorithms, all with different advantages and disadvantages. But I usually try the simplest choice first: I simply pass the two images to ImageCorrelate.

img1 = Import["https://i.stack.imgur.com/laZeT.png"];
img2 = Import["https://i.stack.imgur.com/LAHyF.png"];
corr = ImageAdjust[ImageCorrelate[img1, img2, EuclideanDistance]]

enter image description here

Intuitively, ImageCorrelate shifts img2 by every possible offset, calculates the difference with img1 with that offset, and stores the difference in the output image. So the pixel location with the lowest value in this image corresponds to the offset that minimizes the difference:

minOffset = PixelValuePositions[corr, Min[ImageData[corr]]][[1]] - 
   ImageDimensions[img1]/2;
FlipView[{ImageTransformation[img1, TranslationTransform[minOffset ], 
   PlotRange -> Full], img2}]

enter image description here

If you look at the result of ImageCorrelate, you can see that there's one global minimum. That's the offset where the "U" shapes in the two images are aligned. There's a second local minimum left of it; That's probably where the left "leg" of one U is aligned with the right leg of the other. The shape of the correlation function around the global minimum and the relative correlation values at the local minima give a qualitative impression of the accuracy and reliability of the alignment.

EuclideanDistance compares brightness values. So the different overall brightness in the two images can "move" the minimum. If you don't want that, you can apply a filter to the images that basically removes the low-frequency background lighting changes:

filter = LaplacianGaussianFilter[#, 25] &;
corr = ImageAdjust[
  ImageCorrelate[filter[img1], filter[img2], CosineDistance]]

enter image description here

(where 25 is roughly the width of the U-shape. Details smaller than that and low-frequency changes larger than that are removed by the LoG filter.)

$\endgroup$
3
  • $\begingroup$ +1 Thanks for the explanations - they're very useful for learning about image-processing. Your value is {-140, -12}, @s.s.o's is {149.983, 16.172} to {146.53, 15.249}, @belisarius' is {146.587, 14.2423}, - mine is a bit off at {150.42, 30.8978}. My two manual attempts (using Get Coordinates) were at the lower end of this range, agreeing with yours. $\endgroup$
    – cormullion
    Nov 2, 2013 at 11:21
  • $\begingroup$ Can you explain why it is necessary to use ImageDimensions[img1]/2 in order to get the correct amount to translate the second image? $\endgroup$
    – olliepower
    Mar 21, 2017 at 18:13
  • 1
    $\begingroup$ @olliepower: because the position I've found is the center of the found position, and TranslationTransform wants the top-left corner position. Hence -ImageDimensions[]/2 $\endgroup$ Mar 21, 2017 at 21:06
8
$\begingroup$

I think ImageCorrespondingPoints is a good use of what you need. You can improve the results by more preprocessing steps. You can also check ImageFeatureTrack and FindGeometricTransform.

img1 = Import["https://i.stack.imgur.com/laZeT.png"];
img2 = Import["https://i.stack.imgur.com/LAHyF.png"];
(*preprocess images*)
images = {DeleteSmallComponents@Binarize[img1, .2], 
  DeleteSmallComponents@Binarize[img2, .2]};
(*find matching points*)
matches = 
 ImageCorrespondingPoints[images[[1]], images[[2]], 
  "Transformation" -> "Translation"];
(*Show points on images*)
MapThread[
 Show[#1, Graphics[{Cyan, 
     MapIndexed[Inset[#2[[1]], #1] & , #2]}]] &, {images, matches}]

{{{351.461, 337.156}}, {{501.884, 358.838}}}

enter image description here

Edit 1

You can also use the option "Transformation" -> "Similarity" which adjusts translation, rotation, and scaling of the images.

images = {DeleteSmallComponents@Binarize[FillingTransform@img1, .2], 
   DeleteSmallComponents@Binarize[FillingTransform@img2, .2]};
matches = 
 ImageCorrespondingPoints[images[[1]], images[[2]], 
  "Transformation" -> "Similarity"]
Show[ImageAssemble[images], 
 Graphics[{Red, PointSize[.02], 
   MapThread[
    If[#2 === Missing[], {Cyan, Point[#1]}, 
      Arrow[{#1, #2 + {ImageDimensions[img1][[1]], 0}}]] &, 
    matches]}]]

{{{353.43, 359.493}, {377.258, 359.066}}, {{503.413, 375.665}, {523.788, 374.315}}}

enter image description here

$\endgroup$
7
$\begingroup$

Here you have the transformation function for the images:

{i1, i2} = Import@# & /@ {"https://i.stack.imgur.com/laZeT.png", 
                          "https://i.stack.imgur.com/LAHyF.png"};  
ch = ChanVeseBinarize /@ {i1, i2};
dsc = FillingTransform /@ (DeleteSmallComponents[#, 5000] & /@ ch);
pts = ImageCorrespondingPoints[dsc[[1]], dsc[[2]]];
f   = FindGeometricTransform[pts[[1]], pts[[2]], "Transformation" -> "Translation", Method->"RANSAC"]
GraphicsGrid@{{i1, ImagePerspectiveTransformation[i2, f[[2]], DataRange -> Full]}}

Mathematica graphics

The coefficients of the translation are:

{ {-146.587}, {-14.2423} }

Here you can see how the images overlap after the transformation (Thanks to Simon for the idea)

ColorCombine[{EdgeDetect[dsc[[1]]], 
              ImagePerspectiveTransformation[EdgeDetect[dsc[[2]]], f[[2]], 
              DataRange -> Full]}]

Mathematica graphics

$\endgroup$
3
  • $\begingroup$ Nicely done. You can use ColorCombine[{i1, ImagePerspectiveTransformation[i2, f[[2]], DataRange -> Full]}] to show how well they overlap. $\endgroup$ Nov 1, 2013 at 18:57
  • $\begingroup$ @SimonWoods Thanks! I modified your idea slightly to get a (I think) less cluttered image $\endgroup$ Nov 1, 2013 at 19:05
  • $\begingroup$ Why do you use ChanVeseBinarize ? Is it to adjusts the levels in image, rescaling them to cover the range 0 to 1 ? $\endgroup$ Nov 1, 2013 at 19:15
4
$\begingroup$

Another alternative is to use component measurements. But I don't think this will give very accurate results, because the two images are not so similar in many ways, and the images are hard to 'prepare', having a lot of noise. Anyway, it's good to show approaches that don't work so well... :)

{i1, i2} = 
 Erosion[DeleteSmallComponents[
     ChanVeseBinarize[#, TargetColor -> Gray]], 10] & /@ {image1, image2}
c1 = 1 /. ComponentMeasurements[i1, "MinimalBoundingBox"];
c2 = 1 /. ComponentMeasurements[i2, "MinimalBoundingBox"];

To see some results:

Show[
ImageCompose[image1, {image2, .5}],
 Graphics[{Opacity[0.5], White, Polygon[c1], Polygon[c2], Green, 
 Text[#, #] & /@ c1, Text[#, #] & /@ c2}]]

composition

The best 'pair' of coordinates here are the rightmost corners, giving the translation from image1 to image2 as:

{150.42, 30.8978}

which sadly is only approximately close to Belisarius' result.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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