Steve on Image Processing

July 13th, 2009

A new look for connected component labeling in R2009a - Part 2

Last week I wrote about our desire to reduce the memory used by bwlabel. I also wrote about other issues we hoped to address:

  • The label matrix uses memory proportional to the total number of image pixels. This seems unreasonable when you're working with an image containing only a few object pixels.
  • When bwlabel was used with regionprops (a frequent combination), some computational work was duplicated in both functions.
  • Many users never use the label matrix directly. They only compute it in order to pass it to regionprops. That implies there may be a simpler, more usable interface design.

With so much code out there using bwlabel and regionprops, though, we did not want to introduce any compatibility problems.

Our solution was to introduce two new functions in R2009a, bwconncomp and labelmatrix, as well as a couple of new syntaxes for the existing function regionprops.

The new function bwconncomp finds the connected components of a binary image, just as bwlabel does. But it does not return the result as a label matrix.

BW = [1     1     1     0     0     0     0     0
      1     1     1     0     1     1     0     0
      1     1     1     0     1     1     0     0
      0     0     0     0     0     0     1     0
      0     0     0     0     0     0     1     0
      0     0     0     0     0     0     1     0
      0     1     1     0     0     1     1     0
      0     1     1     0     0     0     0     0];

s = bwconncomp(BW)
s = 

    Connectivity: 8
       ImageSize: [8 8]
      NumObjects: 3
    PixelIdxList: {[9x1 double]  [4x1 double]  [9x1 double]}

As you can see, bwconncomp produces a structure. The critical information for subsequent processing is the PixelIdxList field, which contains the linear indices of the pixels belonging to each object. For example, the 2nd object found comprises four pixels with the following linear indices:

s.PixelIdxList{2}
ans =

    15
    16
    23
    24

A key aspect of this design is that the amount of memory required is proportional to the number of object pixels, not the number of image pixels. For a 1000-by-1000 image containing a single foreground pixel, the output data structure is nicely compact:

BW2 = false(1000, 1000);
BW2(500, 500) = true;
s2 = bwconncomp(BW2)
s2 = 

    Connectivity: 8
       ImageSize: [1000 1000]
      NumObjects: 1
    PixelIdxList: {[499500]}

whos s2
  Name      Size            Bytes  Class     Attributes

  s2        1x1               596  struct              

You can compute the label matrix, if you really need it, using the new function labelmatrix.

L2 = labelmatrix(s2);
whos L2
  Name         Size                Bytes  Class    Attributes

  L2        1000x1000            1000000  uint8              

Notice that L2 is a uint8 matrix. Unlike bwlabel, labelmatrix does adjust the output data type based on the number of objects.

The last related change was to regionprops. As I mentioned before, we've noticed that this is a very common coding pattern:

  L = bwlabel(bw);
  s = regionprops(L, ...);

We've modified the syntax for regionprops so that you can now do this in a one step:

  s = regionprops(bw, ...);

For example:

bw = imread('text.png');
s = regionprops(bw, 'Centroid');

These several new and modified functions, working together, nicely solve several design problems:

  • Reducing memory use
  • Increasing speed (in many cases) by eliminating duplicate computation
  • Converting a two-step workflow to a one-step workflow

We had one last concern, though. It takes code changes to benefit from these enhancements. Users who aren't aware of the new features and don't change their code will never benefit. So what could we do to help users discover the new features?

I'll comment about that next time.


Get the MATLAB code

Published with MATLAB® 7.8

8 Responses to “A new look for connected component labeling in R2009a - Part 2”

  1. Shlomi replied on :

    Great!
    I had to encode (and later decode) my labes matrix as a sparse matrix due its large size and conequent impact on run-time memory!

  2. Shlomi replied on :

    Great idea!
    I previously had to use sparse matrices in order to save the labels matrix(when passing the labels matrix on the way) because of the huge run-time memory it consumed.

  3. SlamJ replied on :

    The update for ‘regionprops’ to calculate the perimeter of each labeled region in a label matrix is very useful. But is there any property of ‘regionprops’ to get the boundary arrays (Example: I need the output of B in the source code given by
    B = regionboundariesmex(double(L),8);)

  4. Steve replied on :

    SlamJ—You could get the ‘Image’ property using regionprops and then call bwboundaries on each.

  5. SlamJ replied on :

    Thanks for ur quick reply. ‘Image’ property using regionprops and calling bwboundaries on each gives the boundary on each region separately . But I need boundaries of each labelled region on a whole.(Example: label matrix with 420 objects should give the boundaries of all 420 objects as a whole and not taking each object separately to give each separated boundary)
    error: Function BWBOUNDARIES expected at most 4 input arguments but was called instead with 420 input arguments.

  6. Jestine replied on :

    Can u please explain the basic idea behind the regionboundariesmex.cpp like how does it search across the image for the first dissimilar pixel and use that location of the pixel as the boundary pixel and how does it trace the remaining?

  7. Steve replied on :

    SlamJ—Sounds to me like the convex hull might be of interest to you. See convhull. I think you may be trying to use regionprops and bwboundaries for something they are not really intended for.

  8. Steve replied on :

    Jestine—I might do a post in the future on contour tracing. In the meantime, take a look at tutorial number 014, “Contour tracing,” on the imageprocessingplace.com tutorials page.

Leave a Reply

Wrap code fragments inside <pre> tags, like this:

<pre class="code">
a = magic(3);
sum(a)
</pre>

If you have a "<" character in your code, either follow it with a space or replace it with "&lt;" (including the semicolon).


Steve Eddins manages the Image & Geospatial development team at The MathWorks and coauthored Digital Image Processing Using MATLAB. He writes here about image processing concepts, algorithm implementations, and MATLAB.

  • Sana: hi steve, could you explain to me how i would be able to use the dir function, to do a loop through a directory...
  • Nishtha: Sir, I have preprocessed the image in following steps: [1] adaptive histogram equalization [2] thresholding...
  • Kristof: I also strongly support the idea. I have just recently bumped into the problem that im2single was not...
  • Steve: David—I’ m glad you found it useful!
  • David Lalejini: I found your example very useful for finding connected nodes in a large set of input pairs. I start...
  • tommy: Dear Steve, I have a question,please if you are kind to help me regarding the accumulator array dimensions of...
  • Steve: Abc—I don’t know how to distinguish the faces. You might try posting your question in the MATLAB...
  • Manju: well if we have a few ovals within each other like in a cell how do we measure the distance from the center...
  • Steve: Manju—What do you mean? How is each region defined?
  • Manju: if we have 2-3 regions within each other how do we measure the regions of each one?

These postings are the author's and don't necessarily represent the opinions of The MathWorks.