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.
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.
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.
What's your favorite thing (book or otherwise) on your bookshelves? Post a comment.
"Section 5.11 of Digital Image Processing Using MATLAB covers spatial transformations. I'm interested in expanding this material to a full chapter. I'd like to use this blog to explore these topics and get your feedback about them."
Now the 2nd edition of Digital Image Processing Using MATLAB is finally available! And it has a brand new chapter called Geometric Transformations and Image Registration. This chapter was my biggest contribution to the new edition.
Here are the chapter contents:
Chapter 6: Geometric Transformations and Image Registration
6.1 Transforming Points
6.2 Affine Transformations
6.3 Projective Transformations
6.4 Applying Geometric Transformations to Images
6.5 Image Coordinate Systems in MATLAB
6.5.1 Output Image Location
6.5.2 Controlling the Output Grid
6.6 Image Interpolation
6.6.1 Interpolation in Two Dimensions
6.6.2 Comparing Interpolation Methods
6.7 Image Registration
6.7.1 Registration Process
6.7.2 Manual Feature Selection and Matching Using cpselect
6.7.3 Inferring Transformation Parameters Using cp2tform
6.7.4 Visualizing Aligned Images
6.7.5 Area-Based Registration
6.7.5 Automatic Feature-Based Registration
I wrote about many of these topics here in the blog first.
Today is the final post in my continental divide series. In earlier posts I have used the problem of computing the US continental divide as a vehicle for exploring data import,
image display scaling, the watershed transform, label matrices, local and regional minima, binary image manipulation, boundary
tracing, and visualization techniques.
For this last post, I thought it would useful to gather together all the computational steps in one short script, starting
with data import and finishing with the visualization.
% Import the tiles e10g and f10g.
data_size = [6000 10800 1]; % The data has 1 band.
precision = 'int16=>int16'; % Read 16-bit signed ints into a int16 array.
header_bytes = 0;
interleave = 'bsq'; % Band sequential. Not critical for 1 band.
byte_order = 'ieee-le';
E = multibandread('e10g', data_size, precision, header_bytes, ...
interleave, byte_order);
F = multibandread('f10g', data_size, precision, header_bytes, ...
interleave, byte_order);
EF = [E, F];
% Crop the data.
dem = EF(1:4000, 6000:14500);
% Form an ocean mask.
ocean_mask = dem == -500;
% Modify the ocean mask so it contains only the two connected components on% the left and right side.
[M, N] = size(dem);
ocean_mask = bwselect(ocean_mask, [1 N], [1 1]);
% Modify the DEM so that its only regional minima are the ocean_mask% pixels.
dem_modified = imimposemin(dem, ocean_mask);
% Compute the watershed transform of the modified DEM.
L = watershed(dem_modified);
% Visualize the result. First, convert the output of watershed to a color% image.
pacific = [0.2 1 0.2];
atlantic = [0.3 0.3 1];
ridge = [1 0 0];
rgb = label2rgb(L, [pacific; atlantic], ridge);
% Superimpose the colored watershed image over the DEM.
imshow(dem, [-500 3000], 'InitialMagnification', 'fit')
hold on
h = imshow(rgb);
set(h, 'AlphaData', 0.2);
% Trace the watershed ridge line and superimpose it as a thick red line.
b = bwboundaries(L == 0, 4);
b1 = b{1}; % There's only 1 boundary found here - the ridge line.
x = b1(:,2);
y = b1(:,1);
plot(x, y, 'r', 'LineWidth', 2);
That's it! I hope you enjoyed this series.
Do you have your own ideas for fun computation and visualization using DEM data? Please comment.
About this Series
This series explores the problem of computing the location of the continental divide for the United States. The divide separates the
Atlantic and Pacific Ocean catchment basins for the North American continent.
As an algorithm development problem, computing the divide lets us explore aspects of data import and visualization, manipulating
binary image masks, label matrices, regional minima, and the watershed transform.
Part 1 - Introduction. Data import and display. multibandread, imshow.
Part 2 - Watershed transform. watershed, label2rgb.
Part 6 - Computing and visualizing the divide. watershed, label2rgb, bwboundaries.
Part 7 - Putting it all together. One script that does everything, from data import through computation and visualization
of the divide.
Data credit: GLOBE Task Team and others (Hastings, David A., Paula K. Dunbar, Gerald M. Elphingstone, Mark Bootz, Hiroshi Murakami, Hiroshi
Maruyama, Hiroshi Masaharu, Peter Holland, John Payne, Nevin A. Bryant, Thomas L. Logan, J.-P. Muller, Gunter Schreier, and
John S. MacDonald), eds., 1999. The Global Land One-kilometer Base Elevation (GLOBE) Digital Elevation Model, Version 1.0.
National Oceanic and Atmospheric Administration, National Geophysical Data Center, 325 Broadway, Boulder, Colorado 80305-3328,
U.S.A. Digital data base on the World Wide Web (URL: http://www.ngdc.noaa.gov/mgg/topo/globe.html) and CD-ROMs.
Today's post in my continental divide series demonstrates the final computational step and shows one procedure for visualizing the Pacific and Atlantic catchment basins.
In my previous post, I modified the DEM (digital elevation model) data set so that its only regional minima were the two oceans.
Since the modified DEM has only two regional minima, we expect to get only two catchment basins when we compute the watershed
transform.
L = watershed(dem_modified);
max(L(:))
ans =
2
I'll use label2rgb to display the Pacific catchment basin in green, the Atlantic catchment basin in blue, and the watershed ridge pixels (the
continental divide) in red.
In a February blog post, I showed how to overlay one image transparently over another. We can use that technique here to achieve an effect similar
to the visualization that Brian Hayes used in his blog post on the continental divide, where the ocean catchment basins are shown shaded with two different colors.
imshow(dem, [-500 3000], 'InitialMagnification', 'fit')
hold on
h = imshow(rgb);
set(h, 'AlphaData', 0.2);
It would be nice if we could make the watershed ridge line itself more visible. For example, if we could use plot, then we could use a thick line. But right now we just have the ridge line represented as zero-valued elements of a raster,
instead of the x and y vectors we would need to pass to plot. We could use find to get the coordinates of the ridge pixels, but we wouldn't get them in the right order. One solution is to use bwboundaries to "trace" the zero-valued ridge pixels. Note that I specify 4-connectivity in the call to bwboundaries below. I've found that I get better results using 4-connectivity when tracing a single-pixel-wide stroke that's not a closed
loop.
b = bwboundaries(L == 0, 4);
b1 = b{1}; % There's only 1 boundary found here - the ridge line.
x = b1(:,2);
y = b1(:,1);
plot(x, y, 'r', 'LineWidth', 2);
I have one more post planned for this series. Next time I'll gather together all the computational steps in a single, short
script.
About this Series
This series explores the problem of computing the location of the continental divide for the United States. The divide separates the
Atlantic and Pacific Ocean catchment basins for the North American continent.
As an algorithm development problem, computing the divide lets us explore aspects of data import and visualization, manipulating
binary image masks, label matrices, regional minima, and the watershed transform.
Part 1 - Introduction. Data import and display. multibandread, imshow.
Part 2 - Watershed transform. watershed, label2rgb.
Part 6 - Computing and visualizing the divide. watershed, label2rgb, bwboundaries.
Part 7 - Putting it all together. One script that does everything, from data import through computation and visualization
of the divide.
Data credit: GLOBE Task Team and others (Hastings, David A., Paula K. Dunbar, Gerald M. Elphingstone, Mark Bootz, Hiroshi Murakami, Hiroshi
Maruyama, Hiroshi Masaharu, Peter Holland, John Payne, Nevin A. Bryant, Thomas L. Logan, J.-P. Muller, Gunter Schreier, and
John S. MacDonald), eds., 1999. The Global Land One-kilometer Base Elevation (GLOBE) Digital Elevation Model, Version 1.0.
National Oceanic and Atmospheric Administration, National Geophysical Data Center, 325 Broadway, Boulder, Colorado 80305-3328,
U.S.A. Digital data base on the World Wide Web (URL: http://www.ngdc.noaa.gov/mgg/topo/globe.html) and CD-ROMs.
My colleague Greg Wilson, creator of the course Software Carpentry, proposed a couple of programming problems as prerequisites for the course. The idea is that if you can solve the problems using any language at all, then you're ready for the course.
Here's Problem 1:
"Write a function that takes a list or array of numbers as input and return the largest number that is adjacent to a zero.
For example, if the input is:
[1, 5, 3, 0, 2, 7, 0, 8, 9, 1 0]
the output is 8."
When I saw this problem I immediately fired off an e-mail to Greg with a MATLAB one-liner:
Of course, one common problem with one-liners tossed off by "experts" is that they are so often wrong. And my answer above
is no exception! If all the nonzero elements adjacent to 0s in the input vector are negative, the above solution would incorrectly
return 0.
Greg raised two questions in response to my e-mail:
How many MATLAB users would be able to understand that solution?
How many MATLAB users would be able to come up with it on their own?
Well, I hope by writing this blog to increase the number of MATLAB and Image Processing Toolbox users who could come up with
it on their own. But let me tackle Greg's first question - how many users would understand the solution?
I think the answer is "not very many." That's because my code does not clearly express my intent. This is a common problem
with one-liners.
I suspect that most MATLAB programmers go through a phase of being overly enamored of the one-liner. (Some never leave that
phase!) Now that I've been in the software development business for quite a while, I've (mostly) lost my fondness for the
one-liner. I'll still write code that way if I'm experimenting at the MATLAB command prompt, but in code that's going to
ship I'm much more likely to break that expression into parts and give each part a meaningful name. I might write it like
this (including the bug fix):
Is this solution understandable? Well, it's more understandable than the first version, but it does assume that the code
reader is familiar with a few key concepts:
MATLAB logical indexing. If you do image processing in MATLAB and you're not familiar with logical indexing, stop right now
and go read my short tutorial on the subject. We'll wait here until you get back.
The use of the term "mask" to mean a logical matrix indicating which elements in another matrix satisfy some property.
The use of binary image dilation (or binary image erosion) to find pixels that are adjacent to a specified set of pixels.
And maybe you need to be comfortable with the idea of applying image processing operators to things that don't appear to be
images, such 1-by-11 vectors. :-)
I have used logical indexing and binary image dilation and erosion in countless ways to do useful things in MATLAB, sometimes
with images and sometimes not. I strongly recommend that you add these ideas to your bag of tricks.
Today, in part 5 of my series on computing the US continental divide, we'll look at a technique called minima imposition. Previously we looked at data import and display, the watershed transform and label matrices, different kinds of minima,
and manipulating binary images to form an ocean mask.
In part 3, we found that our DEM data set for the continental US contained more than half a million regional minima, each
of which was turned into a catchment basin by the watershed transform. We need a way to get just two catchment basins.
Minima imposition is technique that modifies surface heights, pushing some height values upward so that the resulting surface has regional
minima only at specified locations. Minima imposition performs this modification while preserving the local height ordering
relationships between neighboring pixels.
Here's a one-dimensional example to illustrate.
a = [1 2 3 4 3 4 3 2 1]
a =
1 2 3 4 3 4 3 2 1
The vector a has three different regional minima locations:
imregionalmin(a)
ans =
1 0 0 0 1 0 0 0 1
Let's make a mask representing just two locations where we want to have regional minima:
mask = [1 0 0 0 0 0 0 0 1]
mask =
1 0 0 0 0 0 0 0 1
This mask says we only want regional minima at each end of the vector, so we want the regional minima in the middle to go
away somehow. We do this using the Image Processing Toolbox function imimposemin.
The new result has regional minima only at the desired locations. The local minima in the middle of the vector has been pushed
up to the level of the surround elements. (As I write this, I am noticing that the small fractional bump that imimposemin gives to all the middle elements seems possibly unnecessary. This is something I will investigate at another time.)
In my previous post I created a binary image representing an ocean mask:
s = load('continental_divide');
dem = s.dem_cropped;
ocean_mask = s.ocean_mask;
imshow(ocean_mask, 'InitialMagnification', 'fit')
I can use imimposemin, together with the ocean_mask image, to modify the DEM so that the two oceans are the only regional minima.
I'll add the modified DEM to the data I've been collecting in continental_divide.mat:
save continental_dividedem_modified-append
Next time I'll perform the final step in computing the continental divide and show one way to visualize it using transparency.
About this Series
This series explores the problem of computing the location of the continental divide for the United States. The divide separates the
Atlantic and Pacific Ocean catchment basins for the North American continent.
As an algorithm development problem, computing the divide lets us explore aspects of data import and visualization, manipulating
binary image masks, label matrices, regional minima, and the watershed transform.
Part 1 - Introduction. Data import and display. multibandread, imshow.
Part 2 - Watershed transform. watershed, label2rgb.
Part 6 - Computing and visualizing the divide. watershed, label2rgb, bwboundaries.
Part 7 - Putting it all together. One script that does everything, from data import through computation and visualization
of the divide.
Data credit: GLOBE Task Team and others (Hastings, David A., Paula K. Dunbar, Gerald M. Elphingstone, Mark Bootz, Hiroshi Murakami, Hiroshi
Maruyama, Hiroshi Masaharu, Peter Holland, John Payne, Nevin A. Bryant, Thomas L. Logan, J.-P. Muller, Gunter Schreier, and
John S. MacDonald), eds., 1999. The Global Land One-kilometer Base Elevation (GLOBE) Digital Elevation Model, Version 1.0.
National Oceanic and Atmospheric Administration, National Geophysical Data Center, 325 Broadway, Boulder, Colorado 80305-3328,
U.S.A. Digital data base on the World Wide Web (URL: http://www.ngdc.noaa.gov/mgg/topo/globe.html) and CD-ROMs.
A few years ago we introduced the Image Tool for image viewing, navigation, pixel-value display, cropping, etc. Here's a screen shot:
The window on the left is called the "Overview Window." It helps you see where you are when you are zooming in on a large image.
Until the latest toolbox release, R2009a, the Overview Window always appeared whenever you launched the Image Tool. Quite a few customers told us they didn't like this behavior.
If you select the File / Preferences menu in R2009a, you'll see a new "Image Processing" choice. This new preferences panel lets you choose whether the Overview Window always appears when the Image Tool is launched. You can see the Overview Window checkbox circled below.
By default, the checkbox is off, meaning that the Overview Window will not display. To make the Overview Window appear, click on its icon (the first one on the left), or select the Tools / Overview menu.
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.
Recent Comments