1

Given a thresholded image with a small number (1-3) blobs in it, I'd like to pick the blob closest to the centre, which I expect to be a digit:

Before After

I'd like to extract the red rectangle as a separate Mat, since it's closest to the centre (green dot). My first thought is to iterate through all the blobs in the image and take the one with the smallest distance, pixelwise, from its centroid to the image centre. However, I'm not familiar with the vast majority of OpenCV functions (and there are a lot of them!) so I'm wondering if there's a better way.

I can already see a couple of problems with this, though:

  1. If the box on the outside covered 3 or 4 sides, the centroid might be close to the centre even though the box itself is nowhere near the centre. Perhaps I should use a higher-order moment to give more weight to points far away from the centre?
  2. OpenCV has no integrated blob-finding (or connected-component labeling) library that I know of. I could use find contours, but this might be inaccurate for numbers like 6, 8 or 9 with an interior portion.
1''
  • 26,823
  • 32
  • 143
  • 200

6 Answers6

2
  1. Find blobs with findContours
  2. Get their bounding rectangles with boundingRect
  3. At this point, you can do what you wanted
Tae-Sung Shin
  • 20,215
  • 33
  • 138
  • 240
  • How would I pick which bounding rectangle to frame my new image? – 1'' Sep 25 '12 at 01:25
  • It's rectangle so you can easily get location or area of it, right? – Tae-Sung Shin Sep 25 '12 at 01:32
  • In the image above, the bounding rectangle of the blob I don't want takes up most of the image, and so it's fairly centred. That doesn't mean the blob itself is, though. How can I distinguish the two? This is related to my caveat #1, above. – 1'' Sep 25 '12 at 01:35
  • 1
    @1'' You can find smallest rectangle containing your center in that case. Make your algorithm sophisticated slowly but surely. – Tae-Sung Shin Sep 25 '12 at 01:42
1

In your example image, centroids will not work because the other region's centroid is closer to the image center than the 6's centroid. Instead, you should look for the region who contains the closest pixel to the image center.

I would break it down into:

  1. Do region labeling. It is easy (and fun!) to code manually using a fast union-find data structure and the two-pass algorithm described on Wikipedia. You can access the image through the OpenCV Mat interface.
  2. Find the location of the closest nonzero pixel to the center. Start with a simple exhaustive search... only look into optimizations if it's too slow.
  3. Look up this pixel's region in your region-labeled image.

If this isn't good enough, you could look into some kind of a weighted centroid where the pixels closer to the image center have heigher weights.

japreiss
  • 11,111
  • 2
  • 40
  • 77
  • Interesting! I wonder if it might be easier to just floodfill from the nearest white pixel? Although what I'd really want to do is transfer the floodfilled blob to a different image. – 1'' Sep 24 '12 at 18:07
  • Whatever seems easier to you - both would work. Although if you have any other reason to compute a region labeled image, you might as well do it. – japreiss Sep 24 '12 at 18:14
  • What other uses does a region labeled image have? – 1'' Sep 24 '12 at 18:18
  • Lots. It really depends on the application. In machine vision with controlled background and lighting, you can threshold on brightness or color and use region labeling to separate out individual objects. – japreiss Sep 24 '12 at 19:32
  • Coding fun apart, the union-find algorithm is [already implemented in OpenCV](http://docs.opencv.org/modules/core/doc/clustering.html?#template%3Ctypename%20_Tp,%20class%20_EqPredicate%3E%20int%20partition%28const%20vector%3C_Tp%3E&%20vec,%20vector%3Cint%3E&%20labels,%20_EqPredicate%20predicate%29). I dont recommand it for CC labeling especially for large images. – remi Sep 25 '12 at 21:31
  • That function looks like it is not optimized for connected component labeling. It should be `n log(n)`, not `n^2`. – japreiss Sep 26 '12 at 13:19
0

There's no OpenCV function to find the blob whose centroid is closest to a particular point. Anyway, for 1-3 blobs, looping through all of them will be a fast operation.

Osiris
  • 4,195
  • 2
  • 22
  • 52
  • I'm going to be doing this for about 100 images like the one above. In any case, even if speed isn't an issue, there are a few other problems with finding blob centroids (see the edit to my original question). – 1'' Sep 24 '12 at 17:30
0
  1. use either of the following blob counting library...cvBlobslib or use the cvBlob...either of the two are based on a single scan labelling algorithm..so its fast
  2. besides component labeling you can get bounding boxes ,contours, ellipses fitted , blob filtering depending on size of blobs and addition and subtraction of blobs....
rotating_image
  • 3,046
  • 4
  • 28
  • 46
  • I'd rather not include yet another library, especially if it's possible to do without. Thanks though. – 1'' Sep 25 '12 at 01:01
  • do you want a class that takes an image and labels it and its implemented as a static code? – rotating_image Sep 25 '12 at 01:04
  • I'm actually going to try what japreiss suggested and see if it works out. I'm stuck on [this](http://stackoverflow.com/questions/12574937/) problem though. – 1'' Sep 25 '12 at 01:07
  • k...i feel a one scan region labeling will ease your job...but you go ahead and try...good luck... – rotating_image Sep 25 '12 at 01:12
0

You can extract the connected components and compute a label image using findContour/drawContour with regions containing holes. There is a code sample in the documentation that do exactly that, although if speed is an issue you can implement a dedicated method.

To find the CC closest to the image center, you should not rely on the centroid as your regions have complex shapes. You can scan the label image starting from the center in increasing distance and see which connected component you fall on first.

remi
  • 3,914
  • 1
  • 19
  • 37
0
  1. Write a class to do connected component labeling. here's the wiki link on it. You need to create a Disjoint Set class for it to work
  2. Instead of finding the centroid of the blob, find the average sum of distance from pixel to the image center. sum(pixel_i - center)/total_num_pixel_in_the_blob.
DXM
  • 1,249
  • 1
  • 14
  • 22