Search code examples
imagematlabimage-processingedge-detection

How to resample an edge of an image in MATLAB?


I was trying to decrease the number of points of a detected edge of an image but I didn't obtain a good result. I want the result to contain exactly 200 pixels of the edges, but those points must be well chosen so the shape remain very clear. How can I do this?

Here's an example image of what I'm working with:

Here are some results that I have received with the code I wrote:

Code written

function y = echantillonnage(x)
contour_image = x;
[lignes,colonnes]=size(x);
n = nombre/200;
contour_image_200px = contour_image;
ok=0;
for i=1:lignes
    for j=1:colonnes
        if (contour_image_200px(i,j)>0 )
                ok=ok+1;
                if ( mod(ok,round(n))>0 )
                   contour_image_200px(i,j)=0;
                end                 
         end      
    end
end
figure,imshow(contour_image_200px);
%résultat
y = contour_image_200px;
end

Solution

  • What you can do is use bwboundaries to trace the boundaries of the objects / edges then sample from those array of points to decrease the number of edge points. The tracing is done in clockwise order, so you are sure that when you subsample from this array, you will get a semblance of order. However, bwboundaries also returns both outer and inner contour boundaries, so you only need a certain amount of traces from the output. We will talk about that later.

    bwboundaries works for multiple objects, so all you'd have to do is iterate through each object, sample the edge points and write that to an output result. Note that using bwboundaries doesn't even require edges to be found... as long as the object is clean then it isn't necessary. However, I'm not sure as to the purpose of what you're doing, so let's just operate on the edge detected result.

    Let's say we had the following example:

    >> A = false(256, 256);
    >> A(100:170,100:170) = true;
    >> A(3:40,3:40) = true;
    >> A(190:220,200:230) = true;
    >> imshow(A)
    

    We get this image:

    enter image description here

    If we performed an edge detection:

    >> B = edge(A, 'canny');
    >> imshow(B);
    

    We would get this:

    enter image description here

    Now, if you want to do the subsampling, you would call bwboundaries this way:

    [bound,L,N] = bwboundaries(B);
    

    bwboundaries returns a cell array bound of boundaries where each cell is a N x 2 array of spatial coordinates that define the boundary. The first column are the row locations and the second column are the column locations of the boundary points. L is a label matrix that tells you which point each boundary belongs to. We don't need this for your purposes but I might as well talk about it. N is the most important parameter. This defines how many object boundaries there are. This also tells you that the first N cells of bound tells you that those belong to the outer object boundaries.

    As such, you can do the following to subsample your edge points and put them into a new matrix, assuming that your edge image is stored in B. Also, you stated that you want to have 200 points per edge. Let's define that parameter as num_edge_points. However, if you have edges that are less than this amount, then I will assume that you'll just want to have all of the edge points selected.

    out = false(size(B)); %// Initialize output image
    num_edge_points = 200; %// Define number of edge points
    
    %// For each object boundary
    for idx = 1 : N
        boundary = bound{idx}; %// Get boundary
    
        %// Determine how many points we have
        num_pts = size(boundary,1);
    
        %// Generate indices for sampling the boundary
        %// If there are less than the minimum, just choose this amount
        if num_pts < num_edge_points
            ind = 1:num_pts;
        else
            ind = floor(linspace(1,num_pts,num_edge_points));
        end
    
        %// Subsample the edge points
        pts = boundary(ind,:);
    
        %// Mark points in output
        out(sub2ind(size(B), pts(:,1), pts(:,2))) = true;
    end
    

    out will contain your edge image subsampled. To illustrate that we got this right, let's create a new RGB image where we have the edges and the subsampled edge points on top of each other where the subsampled edges are in red:

    out_red = 255*uint8(B);
    out_greenblue = out_red;
    out_greenblue(out) = 0;
    out_rgb = cat(3, out_red, out_greenblue, out_greenblue);
    imshow(out_rgb);
    

    This is what we get (zoomed-in):

    enter image description here

    As you can see, the top and bottom rectangles have the full edge points show as there were less than 200 edge points. However, the one in the middle is sparsely sampled as there are more than 200 edge points, but now we are only displaying 200 of them.


    If you would like a function to help you facilitate this, you can use the following. I've essentially copied all of the code above and the input is a binary image with edges and the output is a binary image with the subsampled edges:

    function [out] = subsample_edge(B)
    
        %// Obtain boundaries for edge image
        [bound,L,N] = bwboundaries(B);
    
        out = false(size(B)); %// Initialize output image
        num_edge_points = 200; %// Define number of edge points
    
        %// For each object boundary
        for idx = 1 : N
            boundary = bound{idx}; %// Get boundary
    
            %// Determine how many points we have
            num_pts = size(boundary,1);
    
            %// Generate indices for sampling the boundary
            %// If there are less than the minimum, just choose this amount
            if num_pts < num_edge_points
                ind = 1:num_pts;
            else
                ind = floor(linspace(1,num_pts,num_edge_points));
            end
    
            %// Subsample the edge points
            pts = boundary(ind,:);
    
            %// Mark points in output
            out(sub2ind(size(B), pts(:,1), pts(:,2))) = true;
        end
    end
    

    If you want to call this function, simply do:

    out = subsample_edge(B);