matlabimage-processingpolygoncorner-detectionnon-convex

Detect corner coordinates of a non-convex polygon in clockwise order MATLAB


I have some images which includes both convex as well as non-convex polygons. Each image contains exactly one polygon. I need to detect the corner coordinates and need to sort them in clock-wise or counter-clockwise order. For convex polygons, I use Harris corner detection for detecting corners and convexhull for sorting the points. But i dont have any idea on how to sort non-convex polygon. As my inputs are images, i think some Image Processing Technique might help to sort them out by moving alongside the edge of the polygon. Is there any way with least complexity?

Example Image:

I have named the corners randomly.

enter image description here

Expected output:

I expect Corner coordinates in this order 1 3 5 9 4 2 8 7 6 10 or 1 10 6 7 8 2 4 9 5 3. You can start at any point, not necessarily 1

Edit 1:

After rayryeng's solution, which works for all convex polygons as well as for some non-convex polygon, there are some non-convex polygons which doesn't go well with his algorithm.

Here is an example

enter image description here


Solution

  • Another approach is to use bwdistgeodesic to find order the corners by their distance along your edge. This should work for any polygon where you can detect a continuous edge.

    We start by loading in the image from Stack Overflow and converting it into a black and white image to make it easier to find the edge

    A = imread('https://i.sstatic.net/dpbpP.jpg');
    A_bw = im2bw(A,100/255);  %binary image
    A_bw1 = imcomplement(A_bw);   %inverted binary image
    

    The bwmorph function provides a lot of options for manipulating black and white images. We're going to use the remove option to find the edge of our polygon, but you could also use another edge detector if you prefer.

    %Find the edges
    A_edges = bwmorph(A_bw, 'remove');
    [edge_x, edge_y] = find(A_edges');
    

    Let's visualize the edges we detected

    figure; imshow(A_edges);
    

    A_edges

    Okay, we have a nice clear continuous edge. Now let's find the corners. I use corner, but you could substitute your favorite corner detector

    A_corners = corner(A_bw1, 'QualityLevel',.3);
    

    Let's visualize our initial corner ordering

    figure; imshow(A_bw1);
    hold on
    plot(A_corners(:,1), A_corners(:,2), 'r.', 'MarkerSize', 18)
    text(A_corners(:,1), A_corners(:,2), strsplit(num2str(1:length(A_corners))), 'Color', 'g', 'FontSize', 24);
    hold off
    

    Corners in Initial order

    Another thing you might not notice, is that they are not directly on the edges. I'll start by finding the closest point along the edge to each corner point, and then I'll visualize corners in red and the closest edge points in green.

    [~, ind] = min(pdist2(A_corners, [edge_x, edge_y]), [], 2);
    A_edge_corners = [edge_x(ind), edge_y(ind)];
    
    figure; imshow(A_edges);
    hold on;
    plot(A_corners(:,1), A_corners(:,2), 'r.', 'MarkerSize', 18)
    plot(A_edge_corners(:,1), A_edge_corners(:,2),'g.', 'MarkerSize', 18)
    hold off;
    

    Corner offset from edge

    To calculate the distance around the edge for each corner, we'll use the corner point approximation, A_edge_corners (green point) on the edge rather than the corner point itself A_corners (red point).

    Now we have all the pieces we need to use bwdistgeodesic. This function finds the distance to a seed point for each non-zero pixel in a black and white image. We are interested in the distance from an initial corner to each point on the edge. Let's try it out.

    % Calculate distance from seed corner
    first_corner = A_edge_corners(1,:);
    D = bwdistgeodesic(A_edges, first_corner(1), first_corner(2));
    figure; imagesc(D);
    

    D

    We're starting from the right most corner and the pixels moving away from the corner increase in value. But this isn't quite what we want. If we order the corners using these values, we end up with an ordering in distance from the initial point.

    [~, corner_order] = sort(D(sub2ind(size(D), A_edge_corners(:,2), A_edge_corners(:,1))));
    A_corners_reorder1 = A_corners(corner_order, :);
    
    figure; imshow(A_bw1);
    hold on
    plot(A_corners_reorder1(:,1), A_corners_reorder1(:,2),'r.', 'MarkerSize', 18)
    text(A_corners_reorder1(:,1), A_corners_reorder1(:,2), strsplit(num2str(1:length(A_corners))), 'Color', 'g', 'FontSize', 24);
    hold off
    

    Corners Reorder from 1st point

    To solve this problem, we just have to break the edge so that the ordering only goes in one direction from the initial point. If you are interested in a clockwise or a counter-clockwise ordering, you would need to break the edge according to a set of rules depending on the orientation of the edge. If the direction doesn't matter you can simply find an adjacent pixel to the initial corner, and break the edge there.

    %Break the edge into one path by removing a pixel adjacent to first corner
    %If the corner is near the edge of the image, you would need to check for
    %edge conditions
    window = A_edges(first_corner(2)-1:first_corner(2)+1, first_corner(1)-1:first_corner(1)+1);
    window(2,2) = 0; %Exclude the corner itself
    [x, y] = find(window, 1);
    A_edges(first_corner(2)+x-2, first_corner(1)+y-2) = 0;  
    
    figure; imshow(A_edges);
    hold on;
    plot(first_corner(1), first_corner(2), 'r.', 'MarkerSize', 18)
    hold off;
    

    Show broken edges

    Now the distance from the initial point along the edge can only follow one path

    %Find order the pixels along edge
    D = bwdistgeodesic(A_edges, first_corner(1), first_corner(2));
    figure; imagesc(D);
    

    D

    This gives us the desired ordering of edges

    [~, corner_order] = sort(D(sub2ind(size(D), A_edge_corners(:,2), A_edge_corners(:,1))));
    A_corners = A_corners(corner_order, :);
    
    figure; imshow(A_bw1);
    hold on
    plot(A_corners(:,1), A_corners(:,2),'r.', 'MarkerSize', 18)
    text(A_corners(:,1), A_corners(:,2), strsplit(num2str(1:length(A_corners))), 'Color', 'g', 'FontSize', 24);
    hold off
    

    Correct ordering

    This method also works on polygons that are unbalanced with respect to the centroid, such as the second demo image.

    second demo image

    For fun, I present a third image, which has a vertex on the opposite side of the centroid, as a clearer example of an unbalanced polygon. My method also correctly parses this image.