43
$\begingroup$

How could I use morphological processing to find circular objects in an image? More specifically, I need to detect road signs.

I need to get the "80 speed limit" sign from this image:

enter image description here

$\endgroup$
4
  • 3
    $\begingroup$ It would help if you described what you have tried so far and where you got stuck. $\endgroup$
    – Verbeia
    Commented Oct 8, 2012 at 10:47
  • 6
    $\begingroup$ This blog post might be useful. $\endgroup$
    – VLC
    Commented Oct 8, 2012 at 11:06
  • 2
    $\begingroup$ Also this one $\endgroup$
    – cormullion
    Commented Oct 8, 2012 at 12:01
  • 2
    $\begingroup$ Because the road signs will rarely be seen head-on, you probably want to generalize the question to detecting elliptical objects--perhaps just ellipses with near-vertical major axes. $\endgroup$
    – whuber
    Commented Oct 8, 2012 at 16:13

3 Answers 3

53
$\begingroup$

The following method doesn't require parameters and discovers also oblique views.

obl[transit_Image] :=
  (SelectComponents[
      MorphologicalComponents[
       DeleteSmallComponents@ChanVeseBinarize[#, "TargetColor" -> Red],
       Method -> "ConvexHull"],
      {"Count", "SemiAxes"}, Abs[Times @@ #2 Pi - #1] < #1/100 &]) & @ transit;

GraphicsGrid[{#, obl@# // Colorize, ImageMultiply[#, Image@Unitize@obl@#]} & /@ 
  (Import /@ ("http://tinyurl.com/" <> # &/@ {"aw74tvc", "aycppg4", "9vnfrko", "bak4uzx"}))]

Mathematica graphics

If you want to detect non-reddish edged ellipses just remove the "TargetColor" -> Red option.

$\endgroup$
16
  • $\begingroup$ That's more like it...! Will it work with red birds on signs, though? :) $\endgroup$
    – cormullion
    Commented Oct 8, 2012 at 16:19
  • $\begingroup$ @cormullion Your raven is one of my examples :D $\endgroup$ Commented Oct 8, 2012 at 17:07
  • $\begingroup$ :) I meant that, if the bird was bright red (like a parrot) it might upset things. But your code is too clever to be fooled, I think! $\endgroup$
    – cormullion
    Commented Oct 8, 2012 at 17:46
  • $\begingroup$ @cormullion Now we only have to find a photograph of a bright red bird sitting on a transit signal and test it :) $\endgroup$ Commented Oct 8, 2012 at 17:58
  • 1
    $\begingroup$ currently doesn't work for the first picture at all - I'm getting black/black as result for it. $\endgroup$
    – user11734
    Commented Mar 10, 2016 at 22:41
27
$\begingroup$

Circular Hough Transform

I have had fun implementing a circular Hough transform based solution for this question (in part using some MMA9 Image3D functionality which has become available in between). By this shape-related approach we can overcome the color restrictions of the approaches tried so far.

The method starts with an edge detection, followed by a circular Hough transform (also see this Java applet demonstration, or these lecture slides, or this paper).

In the following this is demonstrated using a test picture (with inscribed radius numbers) taken from www.markschulze.net/java/hough/. For this implementation the core part is the extensive use of ListConvolve, while the convolution is being done using annulus-shaped kernels.

markschulze = Import["https://i.sstatic.net/XoqbW.png"]

Mathematica graphics

markschulzeedges=EdgeDetect[markschulze, 10, Method -> "Sobel"]

Mathematica graphics

ParallelMap[Image@
 Divide[
  ListConvolve[
   #, 
   ImageData@markschulzeedges, 
   Ceiling[(Length@#)/2]
  ],
  Total[#, 2]
 ] &, 
 Map[
  Function[{r}, DiskMatrix[r] - ArrayPad[DiskMatrix[r - 1], 1]][#] &, 
  Range[14, 18, 1]
 ]
]

{Mathematica graphics, Mathematica graphics, Mathematica graphics, Mathematica graphics, Mathematica graphics }

These five images represent the circular Hough transform within the radius interval [14,18].

In order to utilize the transform for circle or circular area detection this transform is both binarized and labeled. According to the detected coordinates of candidate circles inside the 3D Hough transform volume, radii and image positions are determined, so that masks for the original image can be computed:

HoughCircleDetection[image_Image, radiusmin_Integer: 1, 
   radiusmax_Integer: 40, edgedetectradius_Integer: 10, 
   minfitvalue_Real: .25, radiusstep_Integer: 1, 
   minhoughvoxels_Integer: 4] := 
  Module[{edgeimage, hough3dbin, hough3dbinlabels, coords, arraydim},

   edgeimage = 
    SelectComponents[
     DeleteBorderComponents[
      EdgeDetect[image, edgedetectradius, Method -> "Sobel"]
     ],
     "EnclosingComponentCount", # == 0 &
    ];

   hough3dbin = 
    DeleteSmallComponents[
     Image3D[
      ParallelMap[
       Binarize[Image@
        Divide[
         ListConvolve[#, ImageData@edgeimage, Ceiling[(Length@#)/2]], 
         Total[#, 2]
        ],
        minfitvalue
       ] &,
       Map[
        Function[{r}, DiskMatrix[r] - ArrayPad[DiskMatrix[r - 1], 1]][#] &, 
        Range[radiusmin, radiusmax, radiusstep]
       ]
      ]
     ],
     minhoughvoxels
    ];

   hough3dbinlabels = MorphologicalComponents[hough3dbin];

   coords = 
    ParallelMap[
     Round[Mean[Position[hough3dbinlabels, #]]] &, 
     Sort[Rest@Tally@Flatten@hough3dbinlabels, #1[[2]] > #2[[2]] &][[All, 1]]
    ];

   arraydim = Rest@Dimensions[hough3dbinlabels];

   Print["Radii: ", radiusmin + coords[[All, 1]] - 1];

   ParallelMap[
    Function[{level, offx, offy},
     ImageMultiply[
      image,
      Image@ArrayPad[
       DiskMatrix[radiusmin + level - 1],
       {{offx - radiusmin - level, First@arraydim - offx - radiusmin - level + 1},
        {offy - radiusmin - level, Last@arraydim - offy - radiusmin - level + 1}}
      ]
     ]
    ][Sequence @@ #] &,
    coords
   ]

  ];

Practically it is useful to restrict the method to eligible radii. Also, sometimes parameters, like the edge detection radius, and the minimum fit value might be adapted:

Show[ImageApply[Plus, HoughCircleDetection[#, 14, 18, 10, .3]], 
   ImageSize -> ImageDimensions[#]] &[markschulze]

Radii: {16,15,17,14,14}

Mathematica graphics

The method appears to work quite specifically, though 13 is included here.

Let us see the results for the four images already used above:

Show[ImageApply[Plus, HoughCircleDetection[Import["http://tinyurl.com/aw74tvc"], 30, 50]],
     ImageSize -> 200
]

Radii: {39,33}

Mathematica graphics

Here we find two more or less concentric hits.

Show[ImageApply[Plus, HoughCircleDetection[Import["http://tinyurl.com/aycppg4"], 20, 30, 
   15]],
    ImageSize -> 200
]

Radii: {22}

Mathematica graphics

Oblique views are a matter of luck, of course.

Show[ImageApply[Plus, HoughCircleDetection[Import["http://tinyurl.com/9vnfrko"], 20, 40]],
     ImageSize -> 200
]

Radii: {24,24,24,23,24,24,22,24,24,22,22,24,23,24,22,22,24}

Mathematica graphics

While the No-U-turn sign is missed, the stop sign is included...

Show[ImageApply[Plus, HoughCircleDetection[Import["http://tinyurl.com/bak4uzx"], 20, 60]],
     ImageSize -> 200
]

Radii: {54,38}

Mathematica graphics

For the No-parking sign again the inner and outer circle is found.

For real life applications like road sign detection, for reasons of robustness feature combination is always being recommended.

$\endgroup$
4
  • $\begingroup$ Very nice. Perhaps a link to a good explanation of the Circular Hough Transform may help future readers $\endgroup$ Commented Oct 22, 2013 at 20:15
  • $\begingroup$ Thanks, have added some references, and also have added some more or less didactic example. Sorry for a wrong Floor call inside ListConvolve for kernel alignmend, this was replaced by a Ceiling now, so all resulting images were replaced as well (I noticed a strange shift, this is now gone). $\endgroup$
    – UDB
    Commented Oct 23, 2013 at 14:26
  • $\begingroup$ This is realy great, but I fail at getting the coordinates of the circles: eg. if I take coords[[All,{2,3}]] and place them with points on the image, they don't align with the circles I then obtain with ParallelMap that follows... Probably this is just me being dumb, sorry, but some help? $\endgroup$
    – mgm
    Commented Aug 4, 2015 at 9:49
  • $\begingroup$ @mgm Run this, I guess you will notice how differently coordinates may need to be handled: Module[{image, coords = {{2, 2}, {10, 20}, {20, 30}, {30, 40}}, corrcoords, data}, image = Import["ExampleData/rose.gif"]; corrcoords = Map[{#[[1]], ImageDimensions[image][[2]] + 1 - #[[2]]} &, Reverse[coords, 2]]; Map[(image = ImageCompose[image, Graphics[{Red, PointSize[0.05], Point[{0, 0}]}], # - {1, 0}]) &, corrcoords]; data = ImageData[image]; Map[(data[[#[[1]] - 1 ;; #[[1]] + 1, #[[2]] - 1 ;; #[[2]] + 1]] = Table[{0, 0, 1}, {3}, {3}]) &, coords]; Image@data ] $\endgroup$
    – UDB
    Commented Aug 22, 2015 at 12:39
20
$\begingroup$

Basically, you import the image:

i = Import["http://upload.wikimedia.org/wikipedia/commons/2/2c/Crow_on_the_sign_of_no_parking.jpg"]

image from wikimedia

Tidy it up:

mb = MorphologicalBinarize[i]

mbinarize

Isolate the areas of interest:

cn = ColorNegate[Closing [mb, 10]]

color negate

Use component analysis:

sc = SelectComponents[
  cn, {"Eccentricity", 
   "Circularity"}, #1 < .5  && #2 < 0.8 &];
Colorize[sc]

select components

Now use this information to process your original image...

ImageApply[# * 0.25 &, i, Masking -> ColorNegate[sc]]

apply

Of course, I've cheated in this answer: to process arbitrary images - or to detect crows on road signs - is much much harder!

(I chose this image because, when I wrote this answer, you hadn't supplied your example.)

$\endgroup$
5

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