1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
|
function [p, r, nc, G, xy] = find_components (A,sorted)
%FIND_COMPONENTS finds all connected components of an image.
% Two pixels in an image are in the same connected component if and only if
% they are adjacent and have the same value. "Adjacent" in this context means
% north/south or east/west, not diagonally. That is, A(2,3) is adjacent to
% A(2,2), A(2,4), A(1,3), and A(3,3) only, and is not adjacent to A(3,4).
%
% Let [p r] = find_components(A) where A is m-by-n. The result is a permutation
% p and component boundaries r. p is a permutation of 1:m*n, and refers to the
% linear indexing of A. That is, A(i,j) is refered to as A(i+j*m) in the list
% p. The kth connected component consists of A (p (r(k) : r(k+1)-1)).
% The number of connected components is nc = length (r) - 1. The components
% are ordered by the smallest linear index in each component (assuming you have
% MATLAB 7.5 or later, which uses DMPERM from CSparse; otherwise the ordering
% is not defined).
%
% With a single output argument, c = find_components (A) just returns a list
% of the nodes in the largest component (the component with the largest value
% if ties, and if there are 2 components still tied, return the one containing
% the smallest node index).
%
% The are no restrictions on the image A except that it must be a 2D matrix,
% and the operator "==" must be defined on its entries. If sorting of the
% components by size is requested or if the largest component is requested,
% double(A) must be computable.
%
% Usage:
% c = find_components (A) ; % just return nodes in the largest component
% [p r nc G xy] = find_components (A) ; % sorted by least node number
% [p r nc G xy] = find_components (A, 1) ; % sorted by size, ties by value
% find_components (A) ; % just plot the graph
%
% Example:
%
% A = [ 1 2 2 3
% 1 1 2 3
% 0 0 1 2
% 0 1 3 3 ]
% [p r nc] = find_components (A,1)
% [m n] = size (A) ;
% for k = 1:nc
% a = A (p (r (k))) ;
% fprintf ('\ncomponent %d, size %d, value %d\n', k, r(k+1)-r(k), a) ;
% C = nan (m,n) ;
% C (p (r (k) : r (k+1)-1)) = a ;
% fprintf ('A = \n') ; disp (A) ;
% fprintf ('the component = \n') ; disp (C) ;
% input (': ', 's') ;
% end
%
% The optional outputs G and xy give a graph representation of the problem
% which can be viewed with gplot(G,xy).
%
% See also LARGEST_COMPONENT, FIND_COMPONENTS_EXAMPLE, DMPERM, GPLOT
% find_components, Copyright (c) 2008, Timothy A Davis. All Rights Reserved.
% SPDX-License-Identifier: BSD-3-clause
%-------------------------------------------------------------------------------
% number the nodes
%-------------------------------------------------------------------------------
% K is a matrix containing the linear index into A. For the example above,
%
% K = [ 1 5 9 13
% 2 6 10 14
% 3 7 11 15
% 4 8 12 16 ]
%
% The "nodes" of A are simply their linear indices. For example, A(2,3)
% is called node 10.
[m n] = size (A) ;
N = m*n ; % N = number of nodes in A
K = reshape (1:N, m, n) ;
%-------------------------------------------------------------------------------
% look to the east
%-------------------------------------------------------------------------------
% If A(i,j) == A(i,j+1), then East(i,j) is set to K(i,j+1). That is, East(i,j)
% gives the node number of the Eastern neighbor of the node A(i,j).
%
% In this example, East = [
% 0 9 0 0
% 6 0 0 0
% 7 0 0 0
% 0 0 16 0 ]
%
% because (for example) node 5 has an Eastern neighbor, node 9 (A(5) == A(9)).
East = [(K (:,2:n) .* (A (:,1:n-1) == A (:,2:n))) zeros(m,1)] ;
% E gives the node numbers of all nodes with Eastern neighbors. For example,
% E = [2 3 5 12]' ;
E = find (East) ;
%-------------------------------------------------------------------------------
% look to the south
%-------------------------------------------------------------------------------
% If A(i,j) == A(i+1,j), then South(i,j) is set to K(i+1,j). That is,
% South(i,j) gives the node number of the Southern neighbor of the node A(i,j).
%
% In this example, South = [
% 2 0 10 14
% 0 0 0 0
% 4 0 0 0
% 0 0 0 0 ]
%
% because (for example) node 4 has a Southern neighbor, node 4 (A(3) == A(4)).
% Then S gives the node numbers of all nodes with Southern neighbors.
South = [(K (2:m,:) .* (A (1:m-1,:) == A (2:m,:))) ; zeros(1,n)] ;
S = find (South) ;
%-------------------------------------------------------------------------------
% create the graph G
%-------------------------------------------------------------------------------
% The graph G is N-by-N for an image A of size m-by-n with N=m*n nodes.
% There is an edge between two nodes if they are neighbors (that is, if the
% two nodes are adjacent and have the same value). A diagonal is added to
% the graph so that DMPERM knows that the matrix G does not need to first be
% permuted to reveal a maximum matching ... that step is skipped.
%
% Ignoring the diagonal entries, in this example, G =
%
% (2,1) 1
% (1,2) 1
% (6,2) 1
% (4,3) 1
% (7,3) 1
% (3,4) 1
% (9,5) 1
% (2,6) 1
% (3,7) 1
% (5,9) 1
% (10,9) 1
% (9,10) 1
% (16,12) 1
% (14,13) 1
% (13,14) 1
% (12,16) 1
%
% If drawn as a 4-by-4 mesh, with edges between neighbors, G looks like this:
%
% 1 5 - 9 13
% | | |
% 2 - 6 10 14
%
% 3 - 7 11 15
% |
% 4 8 12 - 16
%
% If the nodes are labeled according the value of A, the graph looks like this:
%
% 1 2 - 2 3
% | | |
% 1 - 1 2 3
%
% 0 - 0 1 15
% |
% 0 1 3 - 3
G = sparse ([K(E) ; K(S)], [East(E) ; South(S)], 1, N, N) ;
clear K East E South S % free up some space in case the problem is large
G = G + G' + speye (N) ;
%-------------------------------------------------------------------------------
% find the connected components of G
%-------------------------------------------------------------------------------
% Note that p==q and r==s, since the matrix G is square with zero-free diagonal.
% nc gives the number of connected components.
[p q r s] = dmperm (G) ; %#ok (s is unused, present for comments)
nc = length (r) - 1 ;
%-------------------------------------------------------------------------------
% sort the components by size, if requested (the rest is all optional)
%-------------------------------------------------------------------------------
if (nargin < 2)
% the default is not to sort the components
sorted = 0 ;
end
if (nargout == 1)
% largest component is requested, so we must sort
sorted = 1 ;
end
if (sorted)
[ignore i] = sortrows ([diff(r)' double(A(p(r(1:end-1)))')], [-1 -2]) ;
if (nargout == 1)
% just return the largest component
c = i (1) ;
p = p (r (c) : r (c + 1) - 1) ;
else
% sort all the components (this can be costly)
p2 = zeros (1,N) ;
r2 = zeros (1,nc+1) ;
k2 = 0 ;
for k = 1:nc
% get the nodes and the size of the kth largest component, c
c = i (k) ;
nodes = p (r (c) : r (c + 1) - 1) ;
csize = length (nodes) ;
% place the nodes in the new output permutation
p2 (k2+1 : k2+csize) = nodes ;
r2 (k) = k2+1 ;
k2 = k2 + csize ;
end
r2 (nc+1) = N+1 ;
p = p2 ;
r = r2 ;
end
end
%-------------------------------------------------------------------------------
% return the XY coordinates, if requested or if the graph is to be plotted
%-------------------------------------------------------------------------------
if (nargout >= 5 || nargout == 0)
x = repmat (1:n, n, 1) ;
y = repmat (m:-1:1, 1, m) ;
xy = [x(:) y(:)] ;
end
%-------------------------------------------------------------------------------
% plot the graph if no outputs requested
%-------------------------------------------------------------------------------
if (nargout == 0)
if (N < 100)
gplot (G, xy, 'o-') ;
else
gplot (G, xy) ;
end
axis ([0 n+1 0 m+1]) ;
title (sprintf ('%d connected components', nc)) ;
end
|