Steve on Image Processing

July 27th, 2009

Five years of MATLAB changes related to image processing

Digital Image Processing Using MATLAB has three coauthors, and we are each responsible for different aspects of writing and other book-related activities. One of my responsibilities is to keep track of changes in MATLAB and Image Processing Toolbox that might affect the book. I look for new features that we might want to mention, as well as behavior changes that might affect existing text in the book.

I was looking at my lists recently and thought they might be interesting to publish here. I normally write about new features in the blog, but I do it one release at a time. We published the first edition of the book in late 2003, so looking at all of my lists gives you a birds-eye view of what's been happening in the products over a five-year period.

Today I'll post my MATLAB lists, and later in the week I'll post my Image Processing Toolbox lists. Note that these lists are not comprehensive; they contain only the items that I thought might be relevant to some topic in the book.

Here's my challenge to readers: Scan these lists and find the oldest "new" feature that you didn't already know about, preferably something that might be useful to you. Let us know about your find by commenting on this post.

Version 7.0

  • Single precision support
  • uipanels
  • dockable figures
  • array editor for viewing structures (useful in compression chapter?)
  • Cell mode and publishing tool
  • Conditional breakpoints
  • Directory reports (code quality issues; todo list; etc.)
  • M-Lint code-checker tool
  • Nondouble arithmetic
  • Single precision linear algebra
  • Single precision FFTs
  • class input for eps function
  • class inputs for realmax, realmin
  • new functions intmax, intmin
  • class inputs for ones, zeros, eye
  • class and size inputs for Inf, NaN
  • isfloat
  • isinteger
  • accumarray
  • mode argument for sort
  • new trig functions for inputs in degrees
  • new calling syntax for function handles
  • anonymous function handles
  • nested functions
  • isscalar
  • isvector
  • strtrim
  • textscan
  • expanded regular expression functions
  • bit functions on unsigned integers
  • compressed MAT-files
  • ftp functions
  • interactive plotting tools
  • data cursors
  • plot annotation (with MATLAB code generation)
  • hggroup
  • hgtransform
  • linkprop
  • axes OuterPosition and TightInset properties (may be useful for fine-tuning plot positioning in our book)
  • GUIDE changes: panels, button groups, ActiveX support, toolbar component, menu editor, key-press detection, edit-text, scroll bar, focus setting
  • uiwait - timeout argument

Version 7.1

  • new function hypot - square root of sum of squares
  • new function mode - most frequent values in sample
  • new function arrayfun - applies a given function to each element of an array. Especially useful for arrays of structures. (possibly useful with regionprops)
  • new function exifread - reads EXIF information from JPEG and TIFF image files
  • new function structfun - applies a given function to each field of a structure
  • new function swapbytes - swaps byte ordering
  • new function typecast - converts data types without changing underlying data
  • cellfun - modified to be able to apply a user-specified function to each element of a cell array
  • Plot Tools enabled on Macintosh
  • Time-series analysis tools

Version 7.2

  • New function idivide provides division similar to A./B on integers except that fractional quotients are rounded to integers according to a specified rounding mode
  • New features for regular expressions:
    dynamic regular expressions (insertion of MATLAB expressions into regular expressions or replacement strings)
    New function for generating literals
    New parsing modes (case-sensitive, single-line, multiline, freespacing)

Version 7.3

  • New MAT-file format capable of storing data larger than 2 GB. Not on by default; must be selected
  • Data cursor text can be programmatically modified
  • Creating GUIs - details and documentation significantly updated
  • mwSize and mwIndex types for use in MEX-file programming

Version 7.4

  • Get online URL of displayed page in help browser
  • Create searchable database for your own toolbox's HTML help files.
  • bsxfun (definitely useful - may want to replace all uses of repmat in DIPUM with calls to bsxfun)
  • divide-by-zero and log-of-zero warnings now off by default
  • new function assert
  • new function verLessThan
  • new inputParser class
  • More GUIDE changes

Version 7.5

  • Help on selection (editor)
  • Code folding in editor - worth recapturing editor screenshots if we have any
  • new function maxNumCompThreads (may want to mention multithreaded computation in MATLAB intro chapter)
  • new 'split' option for regexp
  • new error handling ability - MException - plus related changes to catch
  • new multimedia file reader - mmreader
  • imread has several TIFF-related enhancements:
    LZW, Deflate, and JPEG compression support
    Arbitrary combinations of samples-per-pixel and bits-per-sample
    Increased performance when using 'PixelRegion' parameter. (Might want to have a DIPUM example showing how to read a subset of a TIFF image.)
  • GUIDE changes - custom toolbar editor, icon editor, coordinate readout in layout editor, documentation on how to make GUIDE GUIs interact

Version 7.6 (R2008a)

  • Camera EXIF information incorporated into output from imfinfo
  • FFT functions no longer warn on uint8 input
  • Support for JPEG-2000 import

Version 7.7 (R2008b)

  • Find function names and get help using new function browser
  • View syntax hints while entering statements
  • New formats supported for imread:
    JPEG 2000
    netCDF

Version 7.8 (R2009a)

  • Performance improvements for low-level HDF 5 interface
  • Support for HDF 1.8.1
  • Performance improvement for JPEG 2000 import
  • New syntax for faster reading of TIFF files containing many images
  • New computational geometry classes TriRep, DelaunayTri, TriScatteredInterp
  • New functions now multithreaded: fft, fft2, fftn, ifft, ifft2, ifftn, prod, sum max, min
  • Some Image Processing Toolbox functions moved into MATLAB: rgb2ind, dither, cmunique, cmpermute, imapprox

July 24th, 2009

Scattered data interpolation in R2009a

I want to point you to Damian's second guest blog post on Loren's Art of MATLAB. Scattered data interpolation and data gridding definitely have application to multiple areas in image processing, and the related MATLAB capabilities have significantly improved in R2009a, the latest MATLAB release.

July 20th, 2009

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

I wrote last week about new features in R2009a related to connected component labeling. There are two new functions, bwconncomp and labelmatrix, as well as additional syntaxes for an existing function, regionprops. The features were designed to reduce the amount of memory required to measure geometric properties of connected components (objects) in binary images. In many cases the computational speed is improved as well.

We were concerned that existing users would not discover and use the new features. If users did not change their code, then they would not benefit from the memory and speed improvements.

So we asked the MATLAB M-Lint team for help. M-Lint is the MATLAB code analysis tool that is behind those helpful suggestions that the MATLAB Editor makes about your code. We asked the team to detect the code pattern where the output of bwlabel is passed as the first input argument to regionprops. I showed this coding pattern in Part 1. Here's what the MATLAB Editor now shows for the script is used to create Part 1:

If you hover over the underlined "L" with your mouse, a short message pops up:

And if you click on the short message, you get a full explanation of how to change your code:

As I mentioned in Part 1, the feedback of blog readers was influential in increasing the priority of this issue. I thank you for your many comments on my previous blogs about bwlabel and regionprops.

If you get a chance to use the new features, let us know what you think by commenting on this post.


Get the MATLAB code

Published with MATLAB® 7.8

July 17th, 2009

A chess coach encounters the “300 dpi” mystery

This is a "small world" story for a mid-summer Friday.

Outside work, family, and book writing, I like to play a little tournament chess. I'm just your middle-of-the-pack club player trying to get a bit better, and occasionally I work with a chess coach who goes over my games and tells me about the silly moves I make.

Well, earlier this year my wife called to tell me that my chess coach called the house and was hoping to talk to me that day. That was a bit of a puzzle, since we had no lessons scheduled. What could this be about?

When I called him back, my coach explained that he was working on an advertisement of services to be placed in the program materials for an upcoming national tournament. The printer had notified him that the graphics file he provided wasn't suitable; he needed to send a file that was "300 dpi."

My coach didn't know what that meant, so naturally he did an internet search for terms like "300 dpi." That brought him to my blog post, "Help! My publisher wants a 300 dpi TIFF." After a while, it dawned on him that this "Steve" fellow was actually his student, and he called me to see if I could help.

It made my day to be the "expert" (for a change) in one of our conversations.

July 15th, 2009

Computational geometry in MATLAB R2009a

I've always had a kind of amateur's fascination with computational geometry, and computational geometry problems do arise in image processing from time to time. I've had just enough experience to know how devilishly difficult it can be to robustly compute things that seem intuitively easy.

For the past couple of years I've had the pleasure of talking every so often with Damian Sheehy, a computational geometry expert and software developer on the MATLAB team. I always learn something interesting from him, so I particularly enjoyed seeing his guest blog post today on Loren's Art of MATLAB. He provides an interesting perspective on when you might and might not want to compute certain quantities using exact arithmetic, and he shows off some of the new computational geometry features in R2009a. I encourage you to take a look. And check back again next week; he's got another post planned.

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

July 6th, 2009

A new look for connected component labeling in R2009a

One of the changes we made to the Image Processing Toolbox for R2009a was partially inspired by comments received on this blog. Specifically, several blog readers complained about the memory required by the function bwlabel.

The function bwlabel labels connected components (or objects, or blobs) in a binary image. (I wrote a series of posts a couple of years ago about connected component labeling algorithms.)

I'll illustrate what bwlabel does using a very small binary image.

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]
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

L = bwlabel(BW)
L =

     1     1     1     0     0     0     0     0
     1     1     1     0     3     3     0     0
     1     1     1     0     3     3     0     0
     0     0     0     0     0     0     3     0
     0     0     0     0     0     0     3     0
     0     0     0     0     0     0     3     0
     0     2     2     0     0     3     3     0
     0     2     2     0     0     0     0     0

The output, L, of bwlabel is called a label matrix. A label matrix contains nonnegative integer values. 0s correspond to background pixels in the binary image, and positive values identify each labeled object. For example, the elements of L that equal 2 correspond to the second object found by bwlabel.

L == 2
ans =

     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0
     0     1     1     0     0     0     0     0
     0     1     1     0     0     0     0     0

Very often, the output of bwlabel is used as the input to regionprops, a function that measures geometric properties of regions. For example, we can compute the area of each labeled object:

s = regionprops(L, 'Area')
s = 

3x1 struct array with fields:
    Area

[s.Area]   % comma-separated list syntax for structures
ans =

     9     4     9

As I mentioned in the first paragraph above, we occasionally hear complaints about the amount of memory required by bwlabel. Because bwlabel always returns L as a double-precision matrix, the memory required is 8 bytes per image pixel.

Why is L double-precision? Because bwlabel was introduced to the Image Processing Toolbox at a time when support for integer matrices was just getting off the ground. In 1998 or so, the only non-double data type supported by the Image Processing Toolbox was uint8. It did not seem reasonable to limit label matrices to 255 objects, and we were uncomfortable with a design that called for the data type of L to vary according to the number of objects detected.

If there had been more than minimal support at the time for uint32 or single matrices, we might have chosen one of those types. But there wasn't, so we went with double, and that's what it's been ever since.

The memory complaints have persisted, though, particularly with users who are doing multidimensional image processing. 8 bytes per image pixel seemed to be a high price to pay, especially when there were relatively few foreground pixels.

The usual request was to change the data type L to be uint8 when there were only a few objects. We thought about doing that, but there were other issues we wanted to fix:

1. The memory requirement was proportional to the total number of image pixels instead of the number of object pixels. This would still be the case even we used uint8 in some cases.

2. We knew that an early step inside the implementation of regionprops was to compute a list of all the pixel indices corresponding to each object. This information is known inside the bwlabel computation, but it gets lost the label matrix doesn't represent it directly. Recomputing the pixel indices causes a speed penalty.

3. We knew that most users didn't use the label matrix directly. Often, the only reason for computing it was to hand it off to regionprops.

So we looked for a new design that would address all these issues and that would not cause compatibility problems.

Next time I'll show you what we did.


Get the MATLAB code

Published with MATLAB® 7.8

June 15th, 2009

Getting the MATLAB code from blog posts

Sometimes readers will tell me they had a hard time reproducing results from one of my blog posts, so let me tell you the easiest way to do it. Almost all of my posts that show MATLAB examples have a link at the bottom that says "Get the MATLAB code."

To see how this works, go to my recent "Learning lessons from a one-liner" post, scroll down to the bottom, and click on the "Get the MATLAB code" link just before the comments section.

Loren's Art of MATLAB blog has the same feature.

June 14th, 2009

Reminder about comments on this blog

I would like to remind readers about how I handle comments on this blog. (Please note that each MATLAB Central blog author handles comments somewhat differently.)

I moderate all comments, meaning that new comments will not appear until I have had a chance to review them. I welcome comments that are at least somewhat relevant to the topic of the post. I also welcome general comments about this blog—especially suggestions for new topics!

I generally do not permit comments that are irrelevant to the topic of the post, or that appear to be requests for homework help, or are requests for MATLAB code.

When you post a comment, an e-mail message is immediately sent to me, and I usually make the moderation decision as soon as I see the e-mail.

The conversations that have happened through relevant blog comments and responses have been very valuable, and I encourage you to continue sharing your thoughts here.

June 12th, 2009

References on my shelf

Matt Whitaker asked me in a blog comment recently about the references I keep on my own shelf.

Well, first of all I seem to keep fewer things on my bookshelves as the years go by. When I first started at MathWorks I had two tall bookcases in my office, filled with about 25 linear feet of books and journals. I guess I still had the academic's pack-rat habit. Now I'm down to about 5 feet of books in my office, plus about another 5 feet of journals on the team bookcase nearby.

I don't actually keep any image processing books in my office (except for my own, of course). When we're studying algorithms for possible use in our products, mostly we are looking at journal papers, and most of those are available online. The 5 feet of journal space on the team bookcase is for IEEE Transactions on Pattern Analysis and Machine Intelligence, which for some mysterious reason is still not available in electronic form. [Update: it's available now.] The image processing book that's been the most useful for toolbox development is Soille's Morphological Image Analysis: Principles and Applications. We learned a lot from that book when we were working on morphology functions in Image Processing Toolbox version 3, and I still refer to it regularly.

For the occasional digital signal processing theory question, I have two books from my grad school days: Oppenheim and Schafer's Discrete-Time Signal Processing (signed by Schafer!) and Dudgeon and Mersereau's Multidimensional Digital Signal Processing.

Most of my bookshelves these days are taken up with software development books:

  • General software development books like Code Complete by McConnell, The Pragmatic Programmer by Hunt and Thomas, and Refactoring by Fowler
  • Algorithms in C (in multiple volumes) by Sedgewick
  • Design Patterns by the "gang of four"
  • A pile of C++ books by authors such as Josuttis, Alexandrescu, Meyers, Sutter, and Vandevoorde

But perhaps the most important thing on my shelf is my personal gargoyle, who has been watching over me protectively for many years.

imag0015.jpg

What's your favorite thing (book or otherwise) on your bookshelves? Post a comment.


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.