views:

1703

answers:

4

I have an image in MATLAB:

y = rgb2gray(imread('some_image_file.jpg'));

and I want to do some processing on it:

pic = some_processing(y);

and find the local maxima of the output. That is, all the points in y that are greater than all of their neighbors.

I can't seem to find a MATLAB function to do that nicely. The best I can come up with is:

[dim_y,dim_x]=size(pic);
enlarged_pic=[zeros(1,dim_x+2);
              zeros(dim_y,1),pic,zeros(dim_y,1);
              zeros(1,dim_x+2)];

% now build a 3D array
% each plane will be the enlarged picture
% moved up,down,left or right,
% to all the diagonals, or not at all

[en_dim_y,en_dim_x]=size(enlarged_pic);

three_d(:,:,1)=enlarged_pic;
three_d(:,:,2)=[enlarged_pic(2:end,:);zeros(1,en_dim_x)];
three_d(:,:,3)=[zeros(1,en_dim_x);enlarged_pic(1:end-1,:)];
three_d(:,:,4)=[zeros(en_dim_y,1),enlarged_pic(:,1:end-1)];
three_d(:,:,5)=[enlarged_pic(:,2:end),zeros(en_dim_y,1)];
three_d(:,:,6)=[pic,zeros(dim_y,2);zeros(2,en_dim_x)];
three_d(:,:,7)=[zeros(2,en_dim_x);pic,zeros(dim_y,2)];
three_d(:,:,8)=[zeros(dim_y,2),pic;zeros(2,en_dim_x)];
three_d(:,:,9)=[zeros(2,en_dim_x);zeros(dim_y,2),pic];

And then see if the maximum along the 3rd dimension appears in the 1st layer (that is: three_d(:,:,1)):

(max_val, max_i) = max(three_d, 3);
result = find(max_i == 1);

Is there any more elegant way to do this? This seems like a bit of a kludge.

+6  A: 

If you have the Image Processing Toolbox, you could use the IMREGIONALMAX function:

BW = imregionalmax(y);

The variable BW will be a logical matrix the same size as y with ones indicating the local maxima and zeroes otherwise.

NOTE: As you point out, IMREGIONALMAX will find maxima that are greater than or equal to their neighbors. If you want to exclude neighboring maxima with the same value (i.e. find maxima that are single pixels), you could use the BWCONNCOMP function. The following should remove points in BW that have any neighbors, leaving only single pixels:

CC = bwconncomp(BW);
for i = 1:CC.NumObjects,
  index = CC.PixelIdxList{i};
  if (numel(index) > 1),
    BW(index) = false;
  end
end
gnovice
Thanks! I see that imregionalmax finds maxima that are greater than or equal to their neighbors. Do you know how I can find only those that are greater and not equal to their neighbors?
Nathan Fellman
@Nathan: So, if you were to find a set of neighboring maxima that are equal, would you want to just pick one of them, or exclude all of them?
gnovice
I want to exclude them.
Nathan Fellman
oh... and I fixed the question to show that I'm working with grayscale.
Nathan Fellman
@Nathan: I modified my answer to show you how to remove maxima that have neighbors, but you should also check out Steve Eddins answer using IMDILATE.
gnovice
Steve's answer is indeed more elegant.
Nathan Fellman
+5  A: 

Alternatively, you can use nlfilter and supply your own function to be applied to each neighborhood.

This "find strict max" function would simply check if the center of the neighborhood is strictly greater than all the other elements in that neighborhood, which is always 3x3 for this purpose. Therefore:

I = imread('tire.tif');
BW = nlfilter(I, [3 3], @(x) all(x(5) > x([1:4 6:9])) );
imshow(BW)
Amro
+12  A: 
bw = pic > imdilate(pic, [1 1 1; 1 0 1; 1 1 1]);
Steve Eddins
yep, this one is even faster :)
Amro
+1 I had forgotten how IMDILATE would work with grayscale images ( I usually only use it with logical masks).
gnovice
Can you explain how this works?
Nathan Fellman
@Nathan: IMDILATE operates on each pixel of the grayscale image. The center of the 3-by-3 matrix is positioned at each pixel, and the pixel value is replaced by the maximum value found at the neighboring pixels where there is a value of 1 in the 3-by-3 matrix. The call to IMDILATE therefore returns a new matrix where each point is replaced by the maximum value of its 8 neighbors (zero padded at the edges as needed), and the points where the original matrix is larger indicates a local maxima.
gnovice
`imdilate` goes over each pixel and computes the max of the neighboring pixels centered around it and specified by the mask given (notice the zero in the middle to exclude the pixel itself). Then we compare the resulting image with the original to check whether each pixel is strictly greater than the max of its neighborhood. Make sure to read the documentation page on morphological operations: http://www.mathworks.com/access/helpdesk/help/toolbox/images/f18-12508.html
Amro
Thanks for explaining how this works, gnovice and Amro. I guess my suggestion was a bit cryptic. ;-) One clarification ... imdilate pads by 0 at the edges only for the unsigned integer data types. Generally speaking, imdilate pads by the lowest value for the data type of the input image. That means the technique will work at the edges for all data types - signed and unsigned ints, single-precision and double-precision floating point - even if the array pic has negative values.
Steve Eddins
seems like imdilate is in the image processing toolbox. is there a native matlab solution?
shabbychef
+1  A: 

or, just use the excellent: extrema2.m

Alex Cohen