Fanning Software Consulting

Analyzing Image ROIs

QUESTION: I have previously written about finding the boundary of a region of interest (ROI) or “blob” in an image, and fitting an ellipse to an image to give you some idea of the shape of the ROI. But in putting some of those ideas into practice the other day, I realized there was something I missed telling you. In fact, I didn't realize it myself. I wanted to put these ideas together in a slightly different way, and offer some software to do it, in this article.

ANSWER: The standard way of analyzing ROIs (I will call them “blobs” from now on) is to prepare a bi-level image of 0s and 1s, and use Label_Region to automatically identify the connected regions in the image and assign them a unique integer value. But, of course, Label_Region has problems with blobs on the boundaries of the image, so you have to prepare a special, slightly bigger, image to handle this before you do the analysis. That's kind of a pain in the neck.

And then, in one of the articles above, I suggest you find the indices of each blob by using a Where function on the image that is output from Label_Region. I've done this in the past, and it works fine. But in the past I have worked with relatively small images. The other day, I was working with big images, and this step turns out to be extremely slow. My program, in which I was trying to process about 11,000 blobs, took about 45 minutes to run! Completely unacceptable.

I wish I could tell you I found the answer by reading my own web page (the answer was there!), but alas I had to discover the answer empirically like everyone else. And the answer was, as you can probably guess if you have read many articles on this web page, a Histogram.

Specifically, it is (almost infinitely!) faster to take a Histogram of the output image from Label_Region and find the indices of individual blobs by using the reverse indices of the Histogram command than it is to use the Where function. My 45 minute program ran in 10 seconds after I made this change. In case you missed that, that is 270 times faster than it ran before. (Please don't tell me you have never heard of the Histogram function speeding up IDL code by factors of hundreds or thousands.)

Translated to IDL code, these ideas look like this.

    s = Size(biLevelImage, /DIMENSIONS)     fixLabelRegionImage = BytArr(s + 2)
    fixLabelRegionImage[1,1] = biLevelImage
    labelRegionOutput = Label_Region(fixLabelRegionImage)
    labelRegionOutput = labelRegionOutput[1:s[0], 1:s[1]]
    h = Histogram(labelRegionOutput, MIN=1, REVERSE_INDICES=ri, BINSIZE=1)

To find the indices of, say, the fourth blob (indexed as in all of IDL by a 3), you now simply do this.

    indices4th = ri[ri[3]:ri[4]-1] 

I've made all of this rigamarole significantly easier to do by packaging all these ideas up into an object named Blob_Analyzer. With this program, the code above is simplified to this.

   theBlobs = Obj_New('Blob_Analyzer', biLevelImage)
   indices4th = theBlobs -> GetIndices(3) 

(Note that if you download Blob_Analyzer you will want to also download a new version of Fit_Ellipse, as it had to be changed to work more efficiently with the Blob_Analyzer program.)

Methods exist in this object to find out how many blobs Label_Region found, to return blob statistics, to fit ellipses to blobs, and to print statistical information on all the blobs found to the display or to a file. An example program exists in the file to show you one way Blob_Analyzer can be used. The results of running the example program are shown in the figure below.

The output of the Blob_Analyzer example program.
(A) The original image. (B) The original image opened with MORPH_OPEN. (C) The bilevel input image to the Blob_Analyzer object. (D) The blobs identified by index number, with their perimeters displayed.
 

Calling the ReportStats method on the object results in this output.

   IDL> theBlobs -> ReportStats
  INDEX   NUM_PIXELS   CENTER_X    CENTER_Y   PIXEL_AREA   PERIMETER_AREA   PERIMETER_LENGTH  MAJOR_AXIS   MINOR_AXIS    ANGLE
     0        426        107.89       9.78       106.50          98.00            37.56          12.15        11.29       -8.05
     1        580        151.97      10.22       145.00         134.25            49.21          17.49        11.77       -0.99
     2        812        266.29      15.36       203.00         190.75            52.56          17.88        14.65     -107.48
     3       1438        204.53      43.29       359.50         344.13            70.23          21.68        21.12      -76.47
     4        231        142.10      40.58        57.75          51.38            28.02           8.99         8.24      -69.08
     5        278         29.38      56.06        69.50          62.88            30.26           9.68         9.15      -73.57
     6       1391        142.79      74.15       347.75         332.50            69.11          21.11        20.99     -106.58
     7        261         56.45      73.29        65.25          58.75            29.56           9.45         8.82      -96.80
     8         81        191.00      71.00        20.25          16.50            16.49           5.10         5.10      -90.00
     9        100        208.64      73.64        25.00          20.88            18.19           5.80         5.52      -45.00
    10       1233        233.13      87.28       308.25         294.00            64.70          19.97        19.67     -114.43
    11        311         56.26     106.61        77.75          70.50            32.56          10.62         9.34      -76.65
    12        548         93.97     115.23       137.00         127.50            42.80          13.80        12.64      -72.87
    13        200        232.08     117.79        50.00          44.25            25.73           8.40         7.62       39.20
    14        155        181.97     125.07        38.75          33.50            23.31           8.48         6.10        1.01
    15        540         75.62     140.30       135.00         125.75            43.04          13.50        12.75      -89.02
    16       1441        227.93     151.28       360.25         344.75            70.53          22.37        20.52       82.17
    17        893        111.45     150.51       223.25         211.25            54.87          17.75        16.03      -66.11
    18        328        156.61     151.55        82.00          74.75            33.38          10.65         9.82      -85.27
    19        772        267.46     161.49       193.00         180.38            55.92          19.20        13.02      -74.79
    20        121        198.26     150.00        30.25          25.75            19.90           6.23         6.21     -270.00
    21        100        134.36     161.64        25.00          20.88            18.19           5.80         5.52     -135.00
    22        615        188.07     183.32       153.75         143.25            45.97          15.91        12.74        7.86
    23        270        154.14     187.57        67.50          60.50            30.73          12.79         7.72       -0.30

Many thanks to Ben Tupper for showing me an even more sophisticated version of this code that he wrote for his own analysis projects. I've stolen a couple of good ideas from him. Over time, and as I need them, I hope to incorporate more of those ideas into this program.

Version of IDL used to prepare this article: IDL 7.0.3.

Google
 
Web Coyote's Guide to IDL Programming