Home > matGeom > polygons2d > polygonSkeleton.m

polygonSkeleton

PURPOSE ^

Skeletonization of a polygon with a dense distribution of vertices.

SYNOPSIS ^

function varargout = polygonSkeleton(poly, varargin)

DESCRIPTION ^

 Skeletonization of a polygon with a dense distribution of vertices.

   [V, ADJ] = polygonSkeleton(POLY)
   POLY is given as a N-by-2 array of polygon vertex coordinates. The
   result is given a Nv-by-2 array of skeleton vertex coordinates, and an
   adjacency list, as a NV-by-1 cell array. Each cell contains indices of
   vertices adjacent to the vertex indexed by the cell.

   [V, ADJ, RAD] = polygonSkeleton(POLY)
   Also returns the radius of each vertex, corresponding to the distance
   between the vertex and the closest point of the original contour
   polygon.

   SKEL = polygonSkeleton(POLY)
   Concatenates the results in a struct SKEL with following fields:
   * vertices  the Nv-by-2 array of skeleton vertex coordinates
   * adjList   the adjacency list of each vertex, as a Nv-by-1 cell array.
   * radius    the Nv-by-1 array of radius for each vertex

   Example
     % start from a binary shape
     img = imread('circles.png');
     img = imFillHoles(img);
     figure; imshow(img); hold on;
     % compute a smooth contour
     cntList = imContours(img);
     cnts = smoothPolygon(cntList{1}, 5);
     drawPolygon(cnts, 'g');
     % compute skeleton
     [vertices, adjList] = polygonSkeleton(poly);
     % convert adjacency list to an edge array
     edges = adjacencyListToEdges(adjList);
     % draw the skeleton graph
     drawGraphEdges(vertices, edges);

   See also
     graphs, adjacencyListToEdges

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function varargout = polygonSkeleton(poly, varargin)
0002 % Skeletonization of a polygon with a dense distribution of vertices.
0003 %
0004 %   [V, ADJ] = polygonSkeleton(POLY)
0005 %   POLY is given as a N-by-2 array of polygon vertex coordinates. The
0006 %   result is given a Nv-by-2 array of skeleton vertex coordinates, and an
0007 %   adjacency list, as a NV-by-1 cell array. Each cell contains indices of
0008 %   vertices adjacent to the vertex indexed by the cell.
0009 %
0010 %   [V, ADJ, RAD] = polygonSkeleton(POLY)
0011 %   Also returns the radius of each vertex, corresponding to the distance
0012 %   between the vertex and the closest point of the original contour
0013 %   polygon.
0014 %
0015 %   SKEL = polygonSkeleton(POLY)
0016 %   Concatenates the results in a struct SKEL with following fields:
0017 %   * vertices  the Nv-by-2 array of skeleton vertex coordinates
0018 %   * adjList   the adjacency list of each vertex, as a Nv-by-1 cell array.
0019 %   * radius    the Nv-by-1 array of radius for each vertex
0020 %
0021 %   Example
0022 %     % start from a binary shape
0023 %     img = imread('circles.png');
0024 %     img = imFillHoles(img);
0025 %     figure; imshow(img); hold on;
0026 %     % compute a smooth contour
0027 %     cntList = imContours(img);
0028 %     cnts = smoothPolygon(cntList{1}, 5);
0029 %     drawPolygon(cnts, 'g');
0030 %     % compute skeleton
0031 %     [vertices, adjList] = polygonSkeleton(poly);
0032 %     % convert adjacency list to an edge array
0033 %     edges = adjacencyListToEdges(adjList);
0034 %     % draw the skeleton graph
0035 %     drawGraphEdges(vertices, edges);
0036 %
0037 %   See also
0038 %     graphs, adjacencyListToEdges
0039  
0040 % ------
0041 % Author: David Legland
0042 % e-mail: david.legland@inrae.fr
0043 % INRAE - BIA Research Unit - BIBS Platform (Nantes)
0044 % Created: 2020-05-29,    using Matlab 9.8.0.1323502 (R2020a)
0045 % Copyright 2020 INRAE.
0046 
0047 
0048 %% Voronoi Diagram computation
0049 
0050 % Compute Voronoi Diagram, using polygon vertices as germs.
0051 [V, C] = voronoin(poly);
0052 
0053 % compute number of elements of each array
0054 nVertices   = size(V, 1);
0055 nCells      = size(C, 1);
0056 
0057 % Detection of the vertices located inside the contour
0058 insideFlag = inpolygon(V(:,1), V(:,2), poly(:,1), poly(:,2));
0059 innerVertices = V(insideFlag, :);
0060 
0061 % indices of inner vertices
0062 indsInside = find(insideFlag);
0063 nInnerVertices = length(indsInside);
0064 
0065 % compute map between voronoi vertex indices and skeleton vertex indices.
0066 vertexIndexMap = zeros(nVertices, 1);
0067 for iVertex = 1:length(indsInside)
0068     vertexIndexMap(indsInside(iVertex)) = iVertex;
0069 end
0070 
0071 
0072 %% Compute the topology of the skeleton
0073 %
0074 % Compute the topology as a list of adjacent vertex indices for each vertex
0075 % inside the polygon.
0076 % Need to convert between voronoi indices and skeleton indices.
0077 
0078 % allocate adjacncy list
0079 adjList = cell(nInnerVertices, 1);
0080 vertexGermInds = zeros(nInnerVertices, 1);
0081 
0082 % iterate on voronoi cells to compute skeleton by linking adjacent vertices
0083 % (avoiding first cell which is located at infinity by convention)
0084 for iGerm = 2:nCells
0085     % vertices of current cell
0086     cellVertices = C{iGerm};
0087     nCellVertices = length(cellVertices);
0088     
0089     % iterate on vertices of current cell
0090     for k = 1:nCellVertices
0091         % index of current voronoi vertex
0092         iVertex = cellVertices(k);
0093         
0094         % process only vertices within the contour
0095         if insideFlag(iVertex) == 0
0096             continue;
0097         end
0098         
0099         % convert voronoi vertex index to skeleton vertex index
0100         indV1 = vertexIndexMap(iVertex);
0101         
0102         % update the reference germ associated to current skeleton vertex
0103         vertexGermInds(indV1) = iGerm;
0104         
0105         % compute indices of adjacent vertices (in cell)
0106         iPrev = cellVertices(mod(k - 2, nCellVertices) + 1);
0107         iNext = cellVertices(mod(k, nCellVertices) + 1);
0108         
0109         % keep only the neighbors within the polygon
0110         if insideFlag(iPrev) == 1
0111             adjList{indV1} = [adjList{indV1} vertexIndexMap(iPrev)];
0112         end
0113         if insideFlag(iNext) == 1
0114             adjList{indV1} = [adjList{indV1} vertexIndexMap(iNext)];
0115         end
0116     end
0117 end
0118 
0119 % cleanup to avoid duplicate indices
0120 for iVertex = 1:nInnerVertices
0121     adjList{iVertex} = unique(adjList{iVertex});
0122 end
0123 
0124 
0125 %% Compute radius list
0126 
0127 % for each voronoi vertex inside the polygon, compute the distance to
0128 % original polygon.
0129 % Find indices of germs associated to each vertex.
0130 % By construction, each vertex is the circumcenter of three germs.
0131 radiusList = zeros(nInnerVertices, 1);
0132 for iVertex = 1:nInnerVertices
0133     radiusList(iVertex) = norm(poly(vertexGermInds(iVertex),:) - innerVertices(iVertex,:));
0134 end
0135 
0136 
0137 %% Format output
0138 
0139 if nargout <= 1
0140     % format output to a struct
0141     varargout{1} = struct('vertices', {innerVertices}, 'adjList', {adjList}, 'radius', {radiusList});
0142 elseif nargout == 2
0143     varargout = {innerVertices, adjList};
0144 elseif nargout == 3
0145     varargout = {innerVertices, adjList, radiusList};
0146 end

Generated on Wed 16-Feb-2022 15:10:47 by m2html © 2003-2019