This is the first post in a short series on index techniques that are particularly useful for image processing in MATLAB. I'll start with logical indexing today. Later I'll cover linear indexing, and then a technique I like to call neighbor indexing.
Every MATLAB user is familiar with ordinary matrix indexing notation.
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
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, it's assignment:
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
About every 13.6 days, someone asks this question on comp.soft-sys.matlab:
How do I replace all the NaNs in my matrix B with 0s?
This is generally followed 4.8 minutes later with this reply from one of the newsgroup regulars:
B(isnan(B)) = 0;
B = rand(3,3); B(2, 2:3) = NaN
B = 0.5470 0.1890 0.3685 0.2963 NaN NaN 0.7447 0.1835 0.7802
Replace the NaNs with zeros:
B(isnan(B)) = 0
B = 0.5470 0.1890 0.3685 0.2963 0 0 0.7447 0.1835 0.7802
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 C and D are the same size, and D is a logical matrix.
"Logical" is one of the builtin types, or classes, of MATLAB matrices. Relational operators, such as == or >, produce logical matrices.
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 = 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0
Name Size Bytes Class Attributes D 4x4 16 logical
You can see from the output of whos that the class of the variable D is logical. The logical indexing expression C(D) extracts all the values of C corresponding to nonzero values of D and returns them as a column vector.
ans = 1.0000 0.5000 0.5000
Now we know enough to break down the B(isnan(B)) example to see how it works.
B = rand(3,3); B(2, 2:3) = NaN; nan_locations = isnan(B)
nan_locations = 0 0 0 0 1 1 0 0 0
Name Size Bytes Class Attributes nan_locations 3x3 9 logical
ans = NaN NaN
B(nan_locations) = 0
B = 0.0811 0.4868 0.3063 0.9294 0 0 0.7757 0.4468 0.5108
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 gray-scale 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 = 'http://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.
Or use logical indexing with the complement of the segmentation result to compute the histogram of the background pixels.
Get the MATLAB code
Published with MATLAB® 7.5
18 CommentsOldest to Newest
I was wondering why the logical type uses 1 byte per entry and not 1 bit. Seems like a huge waste of memory for large binary images.
Andreas—It’s not a simple story. It’s tied up in the long history of MATLAB language design evolution. Before MATLAB 5, all MATLAB matrices were 2-D double precision, and “logicalness” was an attribute of those matrices. That’s 8 bytes per binary pixel! Then MATLAB 5 introduced integer array types (including uint8), but at first there was relatively little functional or operator support for them, except in the Image Processing Toolbox. Logicalness was still a numeric array attribute. Gradually, some operators that produced logical arrays began producing uint8 arrays instead of double arrays. These changes were made very cautiously because of obvious compatibility issues. Then in MATLAB 6.5 logical became its own class, instead of an attribute of other classes. Again this change had to be made very carefully to minimize the impact on the existing huge base of internal code, toolbox code (especially in the Image Processing Toolbox), and existing customer MEX code. By keeping to the 1-byte element, much existing internal MATLAB code as well customer code could continue to work with little or no change. A “compatibility layer” was introduced into the MEX API that helped smooth over compatibility issues for a couple of releases. Plus the fact that C had a builtin one-byte data type, but not a builtin one-bit data type, influenced decision making along the way.
My work involves finding suitable pixels in an image and pseudo-randomly selecting those pixels. Considering the selected pixel as centre pixel i have to modify the value of the centre pixel and its 4-neighbours.
The problem is that i must make sure none of neighbours are selected again so the values of already modified 5 pixels dont get remodified. Please suggest something.
Ram—Keep an auxiliary “mask” image (logical matrix) in which pixels already processed are marked with 1s.
I have been trying to figure out a way to convolve specific pixels in an image. Could a logical mask be used to do this? Convolving all the pixels in an image is a waste for my algorithm that only needs to convolve sometimes just one pixel in a loop. The only solution I have found so far is to copy out a 3×3 region, convolve that, and copy the value back. But it isn’t very elegant.
Joshua—You might find roifilt to be helpful.
My images are quite big (169600×241 matrices, 326988800bytes) and when i try to display them it seems I don’t have enough memory or the image is too big to fit in the screen and use the imshow function… How can I make these images easier to handle and take less memory? I am not very familiar with all the available storage formats, which one could I try, any option?
Lydia—If your files are TIFF, and if you have R2007b or R2008a, you can use the PixelRegion parameter of imread to read in subsets of the image, or to read in a subsampled version of the image.
Thanks for directing me to this. It is very well written.
Oliver—I’m glad you found it useful.
I have a question, I am trying to do a function for to create a submatrix (X,Y,Z) from another bigger (X,Y,Z), I need to extract from a range (xmin,xmax; ymin,ymax), I tried for several ways, for example with “for cicle”, “find”, “logical”, but I can’t find the right code, please let me know if you know a best way.
Thank you so much,
Ronny—Sounds like basic MATLAB array indexing:
B = A(xmin:xmax, ymin:ymax, zmin:zmax);
Hi, Steve I think that I didn’t explain me very well, I am sorry for my English. I have the following problem, I have a matrix (3000×3), the points in the matrix are coordinates X,Y,Z, I need to divide my map in 9 similar portions or little windows:
w1 w2 w3
w4 w5 w6
w7 w8 w9
I defined my limits for every window, but I can’t to find the right operation, recently i apply logical operations and the result it doesn’t match with operation with filter on excel. I think that if I use find operator may be work, but I don’t know how can I to create the matrix from index.
If you know the best way please let me know,
Thank a lot,
Hii Steve, I found the solution, I used inpolygon(X,Y,xv,yv) as in the example:
L = linspace(0,2.*pi,6); xv = cos(L)';yv = sin(L)';
xv = [xv ; xv(1)]; yv = [yv ; yv(1)];
x = randn(250,1); y = randn(250,1);
in = inpolygon(x,y,xv,yv);
This function is useful,
I need to index a number of pixels in an image, say 2000 locations in a 1024×1024 uint8 image. What is the fastest way to get the pixel values at these locations?
pixels = image(index); % size(index) = [1,2000]
pixels = image(binary); % size(binary) = [1024,1024]
Or maybe some smarter way? And does the choice of method depend on the ‘pixels_indexed/image_size’-ratio (a method might be the fastest when indexing a few pixels in a large picture but the slowest when indexing many pixels in a small picture)?
Hope you have an answer for this.
Morten—I don’t know. I wouldn’t even bother trying to find out unless detailed profiling demonstrated that it was a true bottleneck in the context of the rest of your algorithm.
As it is now I use
pixels = zeros(size(rows)); for i=1:2000 pixels(i) = image(rows(i),cols(i)); end
because that was the straight to use my input data (rows and cols) for indexing. And the indexing is the bottleneck of my algorithm – I have of cause checked this through profiling. It will take some extensive code modifications to get this right, so I was just asking you, in order to do it right from the start – you might have had a quick answer. But I guess I’ll just go with one of them and a new profiling will show whether the indexing is still the bottleneck.
Morten—I misinterpreted your last comment. I thought you were asking for a comparison of performance between linear and logical indexing because you used a syntax that looked like linear indexing in your question. Anyway, you need to use linear indexing, like this:
pixels = image(sub2ind(size(image), rows, cols));