One of my favorite aspects of MATLAB for image processing is how I can use a binary image to index directly into a grayscale image. The technique is called logical indexing, and I'm going to show you how it works today.
Note: this is an update of a post I originally wrote in 2008.
Let me start with a small example. (As regular readers know, I like to use magic squares for small matrix examples.)
A = magic(5)
A = 17 24 1 8 15 23 5 7 14 16 4 6 13 20 22 10 12 19 21 3 11 18 25 2 9
Every MATLAB user is familiar with ordinary matrix indexing notation.
ans = 7
A(2,3) extracts the 2nd row, 3rd column of the matrix A. You can extract more than one row and column at the same time:
ans = 7 14 16 13 20 22 19 21 3
When an indexing expression appears on the left-hand side of the equals sign, that's an assigment. You are changing one or more of the values of the variable on the left-hand side.
A(5,5) = 100
A = 17 24 1 8 15 23 5 7 14 16 4 6 13 20 22 10 12 19 21 3 11 18 25 2 100
Here is a frequently-asked MATLAB question: How do I replace all the NaNs in my matrix B with 0s?
An experienced MATLAB user will immediately answer:
B(isnan(B)) = 0;
B = rand(3,3); B(2, 2:3) = NaN
B = 0.2217 0.3188 0.0855 0.1174 NaN NaN 0.2967 0.5079 0.8010
Replace the NaNs with zeros:
B(isnan(B)) = 0
B = 0.2217 0.3188 0.0855 0.1174 0 0 0.2967 0.5079 0.8010
The expression B(isnan(B)) is an example of logical indexing. Logical indexing is a compact and expressive notation that's very useful for many image processing operations.
Let's talk about the basic rules of logical indexing, and then we'll reexamine the expression B(isnan(B)).
If C and D are matrices, then C(D) is a logical indexing expression if D is a logical matrix.
Logical is one of the fundamental data types for MATLAB arrays. Relational operators, such as == or >, produce logical arrays automatically.
C = hilb(4)
C = 1.0000 0.5000 0.3333 0.2500 0.5000 0.3333 0.2500 0.2000 0.3333 0.2500 0.2000 0.1667 0.2500 0.2000 0.1667 0.1429
D = C > 0.4
D = 4×4 logical array 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0
If we use D as an index into C with the expression C(D), then we will extract all the values of C corresponding to nonzero values of D and returns them as a column vector. It is equivalent to C(find(D)).
ans = 1.0000 0.5000 0.5000
Now we know enough to break down the B(isnan(B)) expression to see how it works.
B = rand(3,3); B(2, 2:3) = NaN; nan_locations = isnan(B)
nan_locations = 3×3 logical array 0 0 0 0 1 1 0 0 0
ans = NaN NaN
B(nan_locations) = 0
B = 0.0292 0.4886 0.4588 0.9289 0 0 0.7303 0.2373 0.5468
Functions in the Image Processing Toolbox, as well as the MATLAB functions imread and imwrite, follow the convention that logical matrices are treated as binary (black and white) images. For example, when you read a 1-bit image file using imread, it returns a logical matrix:
bw = imread('text.png'); whos bw
Name Size Bytes Class Attributes bw 256x256 65536 logical
This convention, together with logical indexing, makes it very convenient and expressive to use binary images as pixel masks for extracting or operating on sets of pixels.
Here's an example showing how to use logical indexing to compute the histogram of a subset of image pixels. Specifically, given a grayscale image and a binary segmentation, compute the histogram of just the foreground pixels in the image.
Here's our original image:
I = imread('rice.png'); imshow(I)
Here's a segmentation result (computed and saved earlier), represented as a binary image:
url = 'https://blogs.mathworks.com/images/steve/192/rice_bw.png'; bw = imread(url); imshow(bw)
Now use the segmentation result as a logical index into the original image to extract the foreground pixel values.
foreground_pixels = I(bw); whos foreground_pixels
Name Size Bytes Class Attributes foreground_pixels 17597x1 17597 uint8
Finally, compute the histogram of the foreground pixels.
As another example, you could complement the binary image to compute something based on the background pixels.
PS. I expect that this will be the last blog post that I write using R2016b. Keep an eye on the downloads page!
Get the MATLAB code
Published with MATLAB® R2016b
3 CommentsOldest to Newest
While on the topic of indexing in MATLAB, I’d like to ask for your comment on the behavior, where MATLAB allows non-integer indices in some cases, as discussed in this post: http://stackoverflow.com/questions/40455299/ . This has caused much confusion to many of us on SO, and some official explanation could really help.
Dev-iL—I can give a knowledgeable answer, but not an authoritative one. That is, I do know a lot about the history of MATLAB behavior, having been a developer at MathWorks since 1993, but I don’t work directly with the implementation code in this specific area. So, I can give you my own interpretation and educated guess about what has happened here.
In its commercial form, MATLAB has been around for 33 years now. Especially in its earliest days, MATLAB implementers had a tendency to be as permissive as possible with respect to input validation. Over time, we realized that this philosophy wasn’t always the best one for users, and we started making some of the language rules tighter and more regular. One example was the introduction of error messages about invalid indices (noninteger, nonpositive, etc.). However, we couldn’t always tighten up the behavior as much as we liked. Sometimes that was because we discovered that too much user code was exploiting the original behavior. That is one of the reasons you continue to see this kind of behavior variation in some places.
Generally, it is very unlikely that MathWorks developers will describe publicly the internal implementation details related to undocumented behavior, so I recommend against putting any effort into it. Instead, I would advise users to only use integer-valued indices. Users can explicitly call round or floor or whatever to convert the output of the colon operator to be integer-valued.
Thanks again for the comment – it does help clear things up a bit!