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:
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
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:
If we performed an edge detection:
>> B = edge(A, 'canny');
>> imshow(B);
We would get this:
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):
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);