Steve on Image Processing

November 3rd, 2009

The conv function and implementation tradeoffs

A friend from my grad school days (back in the previous millenium) is an electrical engineering professor. Students in his class recently asked him why the conv function is not implemented using FFTs.

I'm not on the team responsible for conv, but I wrote back with my thoughts, and I thought I would share them here as well.

Let's review the basics. Using the typical convolution formula to compute the one-dimensional convolution of a P-element sequence A with Q-element sequence B has a computational complexity of . However, the discrete Fourier transform (DFT) can be used to implement convolution as follows:

1. Compute the L-point DFT of A, where .

2. Compute the L-point DFT of B.

3. Multiply the two DFTs.

4. Compute the inverse DFT to get the convolution.

Here's a simple MATLAB function for computing convolution using the Fast Fourier Transform (FFT), which is simply a fast algorithm for computing the DFT.

type conv_fft
function c = conv_fft(a, b)

P = numel(a);
Q = numel(b);
L = P + Q - 1;
K = 2^nextpow2(L);

c = ifft(fft(a, K) .* fft(b, K));
c = c(1:L);

Note that the code uses the next power-of-two greater than or equal to L, although this is not strictly necessary. The fft function operates in time whether or not L is a power of two.

The overall computational complexity of these steps is . For P and Q sufficiently large, then, using the DFT to implement convolution is a computational win.

So why don't we do it?

There are several technical factors. Let's look at speed, exact computation, and memory.

Speed

One factor is that DFT-based computation is not always faster. Let's do an experiment where we compute the convolution of a 1000-element sequence with another sequence of varying length. (Get the timeit function from the MATLAB Central File Exchange.)

x = rand(1, 1000);
nn = 25:25:1000;

t_normal = zeros(size(nn));
t_fft = zeros(size(nn));

for k = 1:numel(nn)
    n = nn(k);
    y = rand(1, n);
    t_normal(k) = timeit(@() conv(x, y));
    t_fft(k) = timeit(@() conv_fft(x, y));
end
plot(nn, t_normal, nn, t_fft)
legend({'Normal computation', 'FFT-based computation'})

For sequences y shorter than a certain length, called the cross-over point, it's quicker to use the normal computation.

Exact computation

A second consideration is whether the computation is subject to floating-point round-off errors and to what degree. There are applications, for example, where the convolution of integer-valued sequences is computed. For such applications, a user would reasonably expect the output sequence to be integer-valued as well.

To illustrate, here's a simple function that computes n-th order binomial coefficients using convolution:

type binom
function c = binom(n)

c = 1;
for k = 1:n
    c = conv(c, [1 1]);
end

binom(7)
ans =

     1     7    21    35    35    21     7     1

I wrote a variation called binom_fft that is the same as binom except that it calls conv_fft.

format long
binom_fft(7)
ans =

  Columns 1 through 4

   1.000000000000000   6.999999999999998  21.000000000000000  35.000000000000000

  Columns 5 through 8

  35.000000000000000  21.000000000000000   7.000000000000000   0.999999999999996

Whoops! What's going on? The answer is that the FFT-based implementation of convolution is subject to floating-point round-off error.

I imagine that most MATLAB users would consider the output of binom_fft to be wrong.

Memory

The last technical consideration I want to mention is memory. Because of the padding and complex arithmetic involved in the FFT computations, the FFT-based convolution implementation requires a lot more memory than the normal computation. This may not often be a problem for one-dimensional computations, but it can be a big deal for multidimensional computations.

Final thoughts

The technical considerations listed above can all be solved in principle. The implementation could switch to using the normal method for short or integer-valued sequences, for example. And there are FFT-based techniques such as overlap-and-add to reduce the memory load.

But the problem can get quite complicated. Testing floating-point values to see if they are integers, for example, can be slow. Also, I suspect (but have not checked) that a multithreaded implemention of the normal computation will take advantage of multiple cores better than a multithreaded FFT-based method. That would change the cross-over point between the two methods. And the exact cross-over point will vary from computer to computer, making it likely that our implementation would be somewhat slower for some people for some problems.

Overall, my inclination would be to provide FFT-based convolution as a separate function rather than reimplementing conv. And that's what we've done. See the function fftfilt in the Signal Processing Toolbox.

Do you disagree with this approach? Post your comment.


Get the MATLAB code

Published with MATLAB® 7.9

October 20th, 2009

Embedding an ICC profile into a TIFF file

The R2009b release of MATLAB contains a new Tiff class. The primary purpose of this class is to provide lower-level access to creating and modifying image data and metadata in an existing TIFF file.

Several customers have asked for the ability to embed an ICC profile into a TIFF file. The Image Processing Toolbox function iccread can read a profile embedded in a TIFF file, but iccwrite can only write a stand-alone profile file.

Here is code using the new Tiff class to embed a profile in an existing TIFF file. Let's start by rewriting one of the Image Processing Toolbox sample PNG image files as a TIFF file.

rgb = imread('peppers.png');
imwrite(rgb, 'peppers.tif');
s = dir('peppers.tif')
s = 

       name: 'peppers.tif'
       date: '20-Oct-2009 09:14:29'
      bytes: 593880
      isdir: 0
    datenum: 7.3407e+005

Now let's embed the sample profile sRGB.icm into the TIFF file we just made.

Step 1. Read in the raw bytes of the profile file.

fid = fopen('sRGB.icm');
raw_profile_bytes = fread(fid, Inf, 'uint8=>uint8');
fclose(fid);

Step 2. Initialize a Tiff object using 'r+' mode (read and modify).

tif = Tiff('peppers.tif', 'r+')
tif = 

                  TIFF File: 'peppers.tif'
                       Mode: 'r+'
    Current Image Directory: 1
           Number Of Strips: 77
                SubFileType: Tiff.SubFileType.Default
                Photometric: Tiff.Photometric.RGB
                ImageLength: 384
                 ImageWidth: 512
               RowsPerStrip: 5
              BitsPerSample: 8
                Compression: Tiff.Compression.PackBits
               SampleFormat: Tiff.SampleFormat.UInt
            SamplesPerPixel: 3
        PlanarConfiguration: Tiff.PlanarConfiguration.Chunky
                Orientation: Tiff.Orientation.TopLeft

Step 3. Embed the profile bytes as a TIFF tag.

tif.setTag('ICCProfile', raw_profile_bytes);

Step 4. Tell the Tiff object to update the image metadata in the file.

tif.rewriteDirectory();

Step 5. Close the Tiff object.

tif.close();

Now the TIFF file contains the profile. Notice the file size has changed.

s = dir('peppers.tif')
s = 

       name: 'peppers.tif'
       date: '20-Oct-2009 09:14:30'
      bytes: 597828
      isdir: 0
    datenum: 7.3407e+005

And we can read in the profile using iccread.

p = iccread('peppers.tif')
p = 

               Header: [1x1 struct]
             TagTable: {17x3 cell}
            Copyright: 'Copyright (c) 1999 Hewlett-Packard Company'
          Description: [1x1 struct]
      MediaWhitePoint: [0.9505 1 1.0891]
      MediaBlackPoint: [0 0 0]
        DeviceMfgDesc: [1x1 struct]
      DeviceModelDesc: [1x1 struct]
      ViewingCondDesc: [1x1 struct]
    ViewingConditions: [1x1 struct]
            Luminance: [76.0365 80 87.1246]
          Measurement: [1x1 struct]
           Technology: 'Cathode Ray Tube Display'
               MatTRC: [1x1 struct]
          PrivateTags: {}
             Filename: 'peppers.tif'

It would be logical to enhance iccwrite to make this easier for you. We'll look into that, although I'm not sure when that might happen.


Get the MATLAB code

Published with MATLAB® 7.9

October 19th, 2009

Faster morphological reconstruction in R2009b

We've been working for a while now to make Image Processing Toolbox functions run faster. The R2009b release notes mention several performance improvements. We've gotten some feedback, though, that our release notes are pretty vague about the improvements. I can't argue with that impression. We tend to be vague because performance optimization is a very complex topic, and it can be quite difficult to characterize performance changes in a way that is brief, understandable, and accurate for every user's own hardware and data.

But I've decided to start posting more detailed information here about the performance improvements. I have more flexibility (and room!) here than we have with the release notes.

Today I'll tackle imreconstruct, which performs morphological reconstruction. Reconstruction is a very useful operation that I've written about here before. For example, see my post from last year on opening by reconstruction. Several other Image Processing Toolbox functions call imreconstruct, including imclearborder, imfill, imhmax, imhmin, imextendedmax, imextendedmin, and imimposemin.

Let me use the opening by reconstruction example as a benchmark case.

url = 'http://blogs.mathworks.com/images/steve/2008/book_text.png';
text = imread(url);
imshow(text, 'InitialMagnification', 25)
title('918-by-2018 image displayed at 25% magnification')

The example task was to find letters containing vertical strokes by eroding with a vertical structuring element and then performing reconstruction.

se = strel(ones(51, 1));
marker = imerode(text, se);
text2 = imreconstruct(marker, text);
imshow(text2, 'InitialMagnification', 25)
title('Output image displayed at 25% magnification')

So how long does that call to imreconstruct take in R2009b? I'll use my function timeit, which you can download from the MATLAB Central File Exchange.

timeit(@() imreconstruct(marker, text))
ans =

    0.0241

That time is about 45 times faster than the same operation performed in R2009a. Note that I'm running on two-core computer; the improved imreconstruct is multithreaded, so the performance improvement would be greater on a four-core computer, for example.

Now let's time gray-scale reconstruction. I'll make a 1024-by-1024 test image and compute a marker image by subtraction. This kind of operation is often used to suppress small peaks in an image.

I = repmat(imread('rice.png'), 4, 4);
marker = I - 20;
timeit(@() imreconstruct(marker, I))
ans =

    0.0201

This time is about 30 times faster than R2009a, again running on my two-core laptop.

Now for some key caveats you should know. For now, the performance improvements described here only work for 2-D inputs that are uint8, uint16, or single, and only when the specified connectivity is 4 or 8. We'll be working in the future to extend the speed improvements to other inputs.


Get the MATLAB code

Published with MATLAB® 7.9

October 16th, 2009

Friday links

October 15th, 2009

MATLAB Virtual Conference - “the high point of geekism this year”

I spent all of yesterday interacting with visitors to the MATLAB Virtual Conference. It was great fun! A blogger wrote that it was the "high point of geekism and nerdity of this year." (I think that was intended to be a compliment!)

It's not too late to hear the presentations. You can still visit the conference link and listen to the talks there, or you can download them as podcasts.

I started out early in the morning in the Image and Video Processing booth, participating in the group chat there. When it got busy it was a bit challenging to follow all the conversational threads, but I think most people got their questions answered, and I came away with a lot of good product feedback.

Then I went over to hear Tom Kush and Roy Lurie give their "MATLAB Universe" keynote address. If you want to hear about the world-wide impact of MATLAB and get some insight into where we think MATLAB is going, check out this talk. (I didn't get a chance to listen to the other talks yesterday, but I will later.)

The experience reminded me of some of the MATLAB user conferences in the 1990s. One of the only times I've ever heard company president Jack Little tell a joke was during his keynote address at the 1995 user conference in Cambridge, Massachusetts. We had been working for several years on MATLAB 5, which was going to be a huge release. Conference attendees had been promised a preview. So at the opening of the conference, in an enormous ballroom, Jack fired up the development build of MATLAB 5 on the big screen and started to talk about all the exciting new features to come. He said there would be a few changes that might take some getting used to. (We developers were sitting on the back row, holding our breath.) Jack explained that we had decided to adopt RPN (Reverse Polish Notation, popular in HP engineering calculators such as my beloved HP 15C) in MATLAB. He proceeded to type something like:

>> 5 3 +

There was a massive collective gasp! from the audience as we about fell off our chairs in the back trying not to laugh out loud.

To any reader who was there (and is still recovering), I can only say, "We're sorry!" ;-)

OK, back to the present. Later in the day I gave a talk on MATLAB array indexing techniques that are useful for image processing, borrowing heavily from material I originally posted here on the blog. I was afraid the material would be too narrowly focused, but the audience stayed with me to the end and then asked a bunch of great questions about my last topic, neighbor indexing, which was pretty advanced.

For the first five minutes of the talk (which I recorded a couple of weeks ago), I was in a rapidly increasing panic, because a 2-second echo was making the audio completely unintelligible! I bolted out of my office to find someone to help. Kevin calmed me down and helped me figure out that I had a second, forgotten, browser window open to the talk, and the echo was coming there. Boy did I feel silly. Thanks, Kevin!

Readers, I have two questions for you. First, did you attend any of the original MATLAB user conferences in the 90s? Any favorite memories to share?

Second, did you attend the virtual conference yesterday? What did you think? If we do it again, what can we do better next time?

Thanks for taking the time to give us your feedback.

October 12th, 2009

Deploying Image Processing Toolbox apps in R2009b - we goofed

Ack!

We discovered recently that we broke application deployment when using the MATLAB Compiler with Image Processing Toolbox functions in R2009b. The problem happens only on Windows and is caused by some missing shared libraries in the deployed application.

If you use the R2009b MATLAB Compiler on Windows with the Image Processing Toolbox, please see our published bug report for a workaround. (Note: there is no need to apply this workaround to earlier releases.)

We are reviewing our procedures to try to make sure something like this doesn't happen again.

October 9th, 2009

Friday links

Here are some end-of-the-week image processing links for you.

October 7th, 2009

Release notes for old versions

Last week Oliver asked for the release in which CMYK TIFF support was added to imwrite. Oliver then followed up asking for MathWorks to make release notes available for old versions.

In case anyone else is interested, here's one way to navigate to release notes for older versions of MATLAB.

  1. Start at The MathWorks home page.
  2. Click on Support.
  3. Click on Product Documentation.
  4. Click on MATLAB.
  5. Click on MATLAB Release Notes.

Note that release notes are available there for releases back to R14 in 2004. The release notes summarize new features, compatibility considerations, and fixed and known bugs.

Similarly, you can find release notes for the Image Processing Toolbox going back to version 2.2.2 in 2000.

We aren't always perfect about remembering to include everything in the release notes, but this is something we're trying to get better at.

To answer Oliver's original question: Since MATLAB 6.5.1 (R13 SP1 in 2003), imwrite has written a CMYK TIFF when passed an M-by-N-by-4 input array.

October 5th, 2009

MATLAB Virtual Conference on October 14

We're having a MATLAB "virtual conference" on October 14. There'll be presentations, live Q&A, roundtable discussions, chats, etc. It should be fun!

I'm scheduled to do a talk on MATLAB indexing techniques for image processing.

You can register any time on the event overview page. Be sure to check out the presentation schedule to see what might interest you.

October 2nd, 2009

Tab completion for imread, imfinfo, and imwrite in R2009b

OK, I'm embarrassed that I didn't even know about this particular new feature in R2009b. Yesterday I was in another developer's office looking at his latest project. He was typing away in MATLAB when all of a sudden he did something that made me sit up and look closer at the screen. "Hey!" I said. "Do that again!"

He had pressed TAB while typing in the filename input argument to imread, and it automatically completed the filename!

Really, I had no idea they had done this.

Here's a screen snippet.

imread tab completion screen shot

The functions imread, imfinfo, and imwrite all now support tab completion for filenames in your current working folder.


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.

  • Steve: Kezia—Try imrotate.
  • kezia: steve, how to perform rotation of structuring element by 15 degrees. kindly answer my question. thank u kezia...
  • Steve: Tasha—I only accept comments that are relevant to the particular blog post or are questions or comments...
  • Tasha: Steve,I send you a comment here but still didn’t get any reply yet.I did not see my comment posted here...
  • Steve: Carsten—Thanks for your input.
  • Carsten: Another vote for either imtranslate.m, or at least a blurb in the imtransform help why pure translation...
  • Loren Shure: If you look towards the end of the fftfilt program, you will see that there’s a check to see if...
  • Steve: Sonja—My imwritesize submission on the MATLAB Central File Exchange might be helpful. It was posted...
  • Steve: Grant—Sorry, but it won’t be for R2010a. That development deadline has already passed.
  • Sonja: My publisher is wanting images for a new book to be 300 dpi. Only 5 of the 19 images are 300, the rest are...

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