10
$\begingroup$

I have recorded images of a calibration target which I use to determine the resolution of a camera. For that I measure the distance of the parallel bright bars by measuring the brightness variation along the center of the bars perpendicular to their long side. The thickness of the bars is given.

Usually I am not able to align the target exactly in vertical direction.

An example image is given here:

enter image description here

To turn the image into the vertical direction I used a code developed by Markus van Almsick.

Do you know another alternative solution for doing such an alignment automatically?

VerticalAlignment[img_Image] :=
 Module[
  {contours = ImageAdjust@GradientFilter[img, 1], 
   dims = ImageDimensions[img], lines, intersections, phis},
  lines = 
   Map[(# - dims/2) &, ImageLines[contours, MaxFeatures -> 12], {-2}];
  intersections = Apply[
    Replace[
      RegionIntersection[InfiniteLine[#1], InfiniteLine[#2]], 
      {Point[vec : {_, _}] :> ToPolarCoordinates[vec],
       EmptyRegion[2] :> {\[Infinity], ArcTan @@ Subtract @@ #1}}
      ] &,
    Subsets[lines, {2}],
    {1}
    ];
  phis = Select[
    Cases[intersections, {_?(# > Norm[dims] &), phi_} :> 
      Mod[phi, Pi]], (Pi/3 < # < 2 Pi/3) &];
  If[
   phis == {},
   img,
   ImageRotate[img, Pi/2 - Median[phis], "MaxAreaCropping"]
   ]
  ]

VerticalAlignment[image] gives for the upper example:

enter image description here

Now I can do the following (mentioned in the beginning):

ListLinePlot[data[[All, 58, 1]], ImageSize -> Medium]

enter image description here

and determine the peak positions and their distances.

$\endgroup$
1

5 Answers 5

14
$\begingroup$

Using Radon instead of ImageLines@EdgeDetect@. No need for thinning the lines.

i     = ImageAdjust@Import@"https://i.stack.imgur.com/rRCH5.png";
cc    = ComponentMeasurements[MaxDetect[r = Radon@i, .1], "Centroid"];
angle = -cc[[All, 2, 1]] Pi/First@ImageDimensions@r// Mean;
ImageAdjust@ImageRotate[i, angle, Background -> Black] 

Mathematica graphics

The 0.1 in MaxDetect[r = Radon@i, .1] is empirical, but I found it working very well when using Radon[ ]. Otherwise you may find FindThreshold[ ] useful for estimating an ad hoc value.

Showing the alignment:

horline[i_Image, row_] := Graphics[{Orange, Thick, 
                              Line[{{0, row}, {Last@ImageDimensions@i, row}}]}]
With[{j = ImageAdjust@ImageRotate[i, angle, Background -> Black]}, 
                      Show[j, horline[j, #] & /@ cc[[All, 2, 2]]]]

Mathematica graphics

$\endgroup$
7
  • $\begingroup$ is there somewhere a good explanation about MaxDetect ?. I don't understand the documentation. $\endgroup$
    – andre314
    Feb 29, 2016 at 17:37
  • $\begingroup$ @andre Play with this Print[ListPlot[t = Table[Sin[3 x] + 1, {x, 0, 2 Pi, .01}]]]; Manipulate[ ListPlot[{t, MaxDetect[t, x]}, AxesOrigin -> {0, -1}], {x, 0, 2}] I believe it should be clear $\endgroup$ Feb 29, 2016 at 17:55
  • $\begingroup$ @andre You may use cc = ComponentMeasurements[ MaxDetect[r = #, FindThreshold@#] &@Radon@i, "Centroid"]; if you don't like my heuristics $\endgroup$ Feb 29, 2016 at 18:01
  • $\begingroup$ I vote for your solution since you answered everything what I asked and in addition measured the spacing ... thanks $\endgroup$
    – mrz
    Feb 29, 2016 at 18:12
  • 1
    $\begingroup$ @Dr. belisarius It's clear. Thanks ! $\endgroup$
    – andre314
    Feb 29, 2016 at 18:49
6
$\begingroup$

I don't know about the generality of this method, but you seem to already have a general method. This works on the image you provided, and is adapted from the code you posted,

img = Import["https://i.stack.imgur.com/rRCH5.png"];
ImageRotate[img, -ArcTan @@ 
   Flatten@(Differences /@ 
      Transpose[
       First@ImageLines[EdgeDetect@img, 
         MaxFeatures -> 1]]), "MaxAreaCropping"]

enter image description here

Essentially you just need to find one line using ImageLines and then find the angle it makes with the horizontal, then rotate it through the reverse of that angle.

$\endgroup$
1
  • $\begingroup$ Wau ... this is impressing ... thanks $\endgroup$
    – mrz
    Feb 29, 2016 at 13:39
4
$\begingroup$

Another maybe interesting method strike on me with PrincipalComponents:

img = Import["https://i.stack.imgur.com/rRCH5.png"];
inipos = Position[
   ImageData@
    Image@First@Values@ComponentMeasurements[Binarize[img], "Mask"], 
   1., {2}];
mat = FindGeometricTransform[PrincipalComponents[N@inipos], 
    inipos][[2]];
ImageRotate@ImageTransformation[img, mat, PlotRange -> All]

enter image description here

$\endgroup$
3
  • $\begingroup$ @Dr. belisarius How about this? $\endgroup$
    – yode
    Mar 4, 2016 at 20:37
  • 1
    $\begingroup$ Nice :). Also ImageForwardTransformation[#, Last@FindGeometricTransform[PrincipalComponents[#], #, Transformation -> "Rigid"] &@ N@Position[ImageData@MaxDetect[#, .1], 1, {2}], PlotRange -> All] &@img $\endgroup$ Mar 5, 2016 at 7:43
  • $\begingroup$ BTW you can't ping another user like this. S/he ought to be participating in the comments thread below the current answer/question. I saw it while browsing $\endgroup$ Mar 5, 2016 at 7:45
3
$\begingroup$

Another method would be to minimize the height of the rotated and cropped image,

imgheight[a_?NumericQ] := 
 Last@ImageDimensions@ImageCrop@ImageRotate[Binarize@img, a]
NMinimize[{imgheight[a], -π <= a <= π}, a]
(* {92., {a -> -2.78909}} *)

The result,

ImageRotate[img, a /. %[[2]], "MaxAreaCropping"]

enter image description here

$\endgroup$
1
  • $\begingroup$ Also this method is very interesting ... $\endgroup$
    – mrz
    Feb 29, 2016 at 18:08
2
$\begingroup$

So,I'm like the @Dr. belisarius's method with a mysterious Radon.But this solution will solve a more common case.When you original image is nearst vertical.straightimage will rotate it to vertical.If the origional image is nearst horizontal.It will do same thing.

img = ImageAdjust@Import@"https://i.stack.imgur.com/rRCH5.png";
imgrote = 
 ImageRotate[img, #, "SameRatioCropping"] & /@ Range[0, 3, 0.5]

enter image description here

straightimage[img_] := 
 Module[{lines = 
    ImageLines@
     DeleteSmallComponents[
      EdgeDetect[img, Method -> {"Canny", "StraightEdges" -> 0.4}]], 
   list},
  ImageRotate[img, 
   Mean@Select[
     list = Mod[-#, Pi/2, -Pi/4] & /@ ArcTan @@@ Subtract @@@ lines, 
     Chop[First@Commonest@Round[list, 1/1000] - #, 1/1000] === 0 &], 
   Background -> Transparent]]

Example:

straightimage /@ imgrote

enter image description here

$\endgroup$
1
  • $\begingroup$ Thank you .. this is very interesting $\endgroup$
    – mrz
    Mar 4, 2016 at 9:18

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.