Search code examples
matlabedge-detection

How to find all edge pixels around one region in binary image?


The region's hole has been filled and I want to find all edge pixels in anti-clockwise direction. At first, my solution is

1、 find all pixel at edge

2、get x-direction between centroid and the pixel.

3、sort it.

How to improve it? Image and code as follows, but when I test code, it found that the concave and bulge make it useless because for at these angles there are possible many points.

enter image description here

function nAngle = create_angle_array(nPosition,centroid) 
a = repmat(centroid,[size(nPosition,1),1]) 
nAngle = mod(angle((nPosition-a)*[1;1j]),2*pi) 
end


se = strel('rect',[3,3]);
erodeImage = imerode(binaryImage,se);
erodeImageLeft = binaryImage - erodeImage;
% countPixel = sum(erodeImageLeft(:)== true);
[edgeRow_a,edgeCol_a] = find(erodeImageLeft);
if isscalar(edgeRow_a)
   edgeRow = [edgeRow_a];
else
    edgeRow = edgeRow_a;
end
if isscalar(edgeCol_a)
    edgeCol = [edgeCol_a];
else
    edgeCol = edgeCol_a;
end
disp(edgeRow);
disp(edgeCol);
angleValue = create_angle_array(cat(2,edgeRow,edgeCol),centroid);
disp(angleValue);
nPixel = cat(2,angleValue,edgeRow,edgeCol);
fprintf('size(nPixelA) is [%s]\n', int2str(size(nPixel)));
disp(nPixel)
nEdgePixel = sortrows(nPixel)

Solution

  • As you've seen, sorting the pixels along an axis does not guarantee a consistent ordering. A more reliable way is to take a pixel and find the next pixel adjacent to it in the proper direction.

    To do this, we need to search the adjacent pixels in the proper order. For a counter-clockwise ordering, we'll define the directions as:

        1  0  7
         \ | /
          \|/
        2--+--6
          /|\
         / | \
        3  4  5
    

    For the current pixel, we will start searching for the next pixel in the direction of the previous pixel plus 1 (modulo 8). So if the previous pixel was in direction 6, we'll first look in direction 7, then 0, then 1, until we find the next perimeter pixel. Then repeat until we reach the starting pixel again.

    When we select the starting pixel, we don't have a "previous" pixel, so the easiest way to ensure that we don't miss any pixels is to start on one of the extremes (say, leftmost). Then set the direction of the "previous" pixel to the direction you know there are no more pixels, that is, to the left or direction 2. So,

    1. Set current_pixel and assign previous_direction as above. Add current_pixel to perimeter_list.
    2. Repeat:
    3.     Starting at previous_direction + 1 (modulo 8), search adjacent pixels in order until you find another perimeter pixel.
    4.     If the new pixel is equal to the starting pixel, break.
    5.     Add new perimeter pixel to list. If the direction the new pixel was found in is d, set previous_direction to d+4 mod 8. Set the current_pixel to the newly-found pixel.

    This will find all of the perimeter pixels of a region in the proper order without having to explicitly find the perimeter pixels first. Any "spikes", or one pixel wide lines, protruding from the region will list the pixels twice, once traversing the line in each direction, so your final list might be longer than the number of perimeter pixels. You can also skip filling the holes, unless you need them filled for a future step.

    Some things to look out for are making sure that you don't look outside the bounds of the image, and getting MATLAB's 1-based array indexing to work with mod. Personally, I have a mod1 function to do one-based modulo. If you do this, just change the direction numbers to be 1 to 8 instead of 0 to 7. I chose to start with the leftmost pixel because that's what find(bwimg, 1) is going to return.