-
Notifications
You must be signed in to change notification settings - Fork 0
/
boundaries.m
173 lines (149 loc) · 5.64 KB
/
boundaries.m
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
function B = boundaries(BW, conn, dir)
%BOUNDARIES Trace object boundaries.
% B = BOUNDARIES(BW) traces the exterior boundaries of objects in
% the binary image BW. B is a P-by-1 cell array, where P is the
% number of objects in the image. Each cell contains a Q-by-2
% matrix, each row of which contains the row and column coordinates
% of a boundary pixel. Q is the number of boundary pixels for the
% corresponding object. Object boundaries are traced in the
% clockwise direction.
%
% B = BOUNDARIES(BW, CONN) specifies the connectivity to use when
% tracing boundaries. CONN may be either 8 or 4. The default
% value for CONN is 8.
%
% B = BOUNDARIES(BW, CONN, DIR) specifies the direction used for
% tracing boundaries. DIR should be either 'cw' (trace boundaries
% clockwise) or 'ccw' (trace boundaries counterclockwise). If DIR
% is omitted BOUNDARIES traces in the clockwise direction.
% Copyright 2002-2004 R. C. Gonzalez, R. E. Woods, & S. L. Eddins
% Digital Image Processing Using MATLAB, Prentice-Hall, 2004
% $Revision: 1.6 $ $Date: 2003/11/21 14:22:07 $
if nargin < 3
dir = 'cw';
end
if nargin < 2
conn = 8;
end
L = bwlabel(BW, conn);
% The number of objects is the maximum value of L. Initialize the
% cell array B so that each cell initially contains a 0-by-2 matrix.
numObjects = max(L(:));
if numObjects > 0
B = {zeros(0, 2)};
B = repmat(B, numObjects, 1);
else
B = {};
end
% Pad label matrix with zeros. This lets us write the
% boundary-following loop without worrying about going off the edge
% of the image.
Lp = padarray(L, [1 1], 0, 'both');
% Compute the linear indexing offsets to take us from a pixel to its
% neighbors.
M = size(Lp, 1);
if conn == 8
% Order is N NE E SE S SW W NW.
offsets = [-1, M - 1, M, M + 1, 1, -M + 1, -M, -M-1];
else
% Order is N E S W.
offsets = [-1, M, 1, -M];
end
% next_search_direction_lut is a lookup table. Given the direction
% from pixel k to pixel k+1, what is the direction to start with when
% examining the neighborhood of pixel k+1?
if conn == 8
next_search_direction_lut = [8 8 2 2 4 4 6 6];
else
next_search_direction_lut = [4 1 2 3];
end
% next_direction_lut is a lookup table. Given that we just looked at
% neighbor in a given direction, which neighbor do we look at next?
if conn == 8
next_direction_lut = [2 3 4 5 6 7 8 1];
else
next_direction_lut = [2 3 4 1];
end
% Values used for marking the starting and boundary pixels.
START = -1;
BOUNDARY = -2;
% Initialize scratch space in which to record the boundary pixels as
% well as follow the boundary.
scratch = zeros(100, 1);
% Find candidate starting locations for boundaries.
[rr, cc] = find((Lp(2:end-1, :) > 0) & (Lp(1:end-2, :) == 0));
rr = rr + 1;
for k = 1:length(rr)
r = rr(k);
c = cc(k);
if (Lp(r,c) > 0) & (Lp(r - 1, c) == 0) & isempty(B{Lp(r, c)})
% We've found the start of the next boundary. Compute its
% linear offset, record which boundary it is, mark it, and
% initialize the counter for the number of boundary pixels.
idx = (c-1)*size(Lp, 1) + r;
which = Lp(idx);
scratch(1) = idx;
Lp(idx) = START;
numPixels = 1;
currentPixel = idx;
initial_departure_direction = [];
done = 0;
next_search_direction = 2;
while ~done
% Find the next boundary pixel.
direction = next_search_direction;
found_next_pixel = 0;
for k = 1:length(offsets)
neighbor = currentPixel + offsets(direction);
if Lp(neighbor) ~= 0
% Found the next boundary pixel.
if (Lp(currentPixel) == START) & ...
isempty(initial_departure_direction)
% We are making the initial departure from
% the starting pixel.
initial_departure_direction = direction;
elseif (Lp(currentPixel) == START) & ...
(initial_departure_direction == direction)
% We are about to retrace our path.
% That means we're done.
done = 1;
found_next_pixel = 1;
break;
end
% Take the next step along the boundary.
next_search_direction = ...
next_search_direction_lut(direction);
found_next_pixel = 1;
numPixels = numPixels + 1;
if numPixels > size(scratch, 1)
% Double the scratch space.
scratch(2*size(scratch, 1)) = 0;
end
scratch(numPixels) = neighbor;
if Lp(neighbor) ~= START
Lp(neighbor) = BOUNDARY;
end
currentPixel = neighbor;
break;
end
direction = next_direction_lut(direction);
end
if ~found_next_pixel
% If there is no next neighbor, the object must just
% have a single pixel.
numPixels = 2;
scratch(2) = scratch(1);
done = 1;
end
end
% Convert linear indices to row-column coordinates and save
% in the output cell array.
[row, col] = ind2sub(size(Lp), scratch(1:numPixels));
B{which} = [row - 1, col - 1];
end
end
if strcmp(dir, 'ccw')
for k = 1:length(B)
B{k} = B{k}(end:-1:1, :);
end
end