-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcleanedgelist.m
365 lines (289 loc) · 13 KB
/
cleanedgelist.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
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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
% CLEANEDGELIST - remove short edges from a set of edgelists
%
% Function to clean up a set of edge lists generated by EDGELINK so that
% isolated edges and spurs that are shorter that a minimum length are removed.
% This code can also be use with a set of line segments generated by LINESEG.
%
% Usage: nedgelist = cleanedgelist(edgelist, minlength)
%
% Arguments:
% edgelist - a cell array of edge lists in row,column coords in
% the form
% { [r1 c1 [r1 c1 etc }
% r2 c2 ...
% ...
% rN cN] ....]
% minlength - minimum edge length of interest
%
% Returns:
% nedgelist - the new, cleaned up set of edgelists
%
% See also: EDGELINK, DRAWEDGELIST, LINESEG
% Copyright (c) 2006-2007 Peter Kovesi
% School of Computer Science & Software Engineering
% The University of Western Australia
% pk at csse uwa edu au
% http://www.csse.uwa.edu.au/
%
% Permission is hereby granted, free of charge, to any person obtaining a copy
% of this software and associated documentation files (the "Software"), to deal
% in the Software without restriction, subject to the following conditions:
%
% The above copyright notice and this permission notice shall be included in
% all copies or substantial portions of the Software.
%
% The Software is provided "as is", without warranty of any kind.
%
% December 2006 Original version
% February 2007 A major rework to fix several problems, hope they really are
% fixed!
function nedgelist = cleanedgelist(edgelist, minlength)
Nedges = length(edgelist);
Nnodes = 2*Nedges;
% Each edgelist has two end nodes - the starting point and the ending point.
% We build up an adjacency/connection matrix for each node so that we can
% determine which, if any, edgelists are connected to a node. We also
% maintain an adjacency matrix for the edges themselves.
%
% It is tricky maintaining all this information but it does allow the
% code to run much faster.
% First extract the end nodes from each edgelist. The nodes are numbered
% so that the start node has number 2*edgenumber-1 and the end node has
% number 2*edgenumber
node = zeros(Nnodes, 2);
for n = 1:Nedges
node(2*n-1,:) = edgelist{n}(1,:);
node(2*n ,:) = edgelist{n}(end,:);
end
% Now build the adjacency/connection matrices.
A = zeros(Nnodes); % Adjacency matrix for nodes
B = zeros(Nedges); % Adjacency matrix for edges
for n = 1:Nnodes-1
for m = n+1:Nnodes
% If nodes m & n are connected
A(n,m) = node(n,1)==node(m,1) && node(n,2)==node(m,2);
A(m,n) = A(n,m);
if A(n,m)
edgen = fix((n+1)/2);
edgem = fix((m+1)/2);
B(edgen, edgem) = 1;
B(edgem, edgen) = 1;
end
end
end
% If we sum the columns of the adjacency matrix we get the number of
% other edgelists that are connected to an edgelist
Nconnections = sum(A); % Connection count array for nodes
Econnections = sum(B); % Connection count array for edges
% Check every edge to see if any of its ends are connected to just one edge.
% This should not happen, but occasionally does due to a problem in
% EDGELINK. Here we simply merge it with the edge it is connected to.
% Ultimately I want to be able to remove this block of code.
% I think there are also some cases that are (still) not properly handled
% by CLEANEDGELIST and there may be a case for repeating this block of
% code at the end for another final cleanup pass
for n = 1:Nedges
if ~B(n,n) && ~isempty(edgelist{n}) % if edge is not connected to itself
[spurdegree, spurnode, startnode, sconns, endnode, econns] = connectioninfo(n);
if sconns == 1
node2merge = find(A(startnode,:));
mergenodes(node2merge,startnode);
end
if ~isempty(edgelist{n}) % If we have not removed this edge in
% the code above check the other end.
if econns == 1
node2merge = find(A(endnode,:));
mergenodes(node2merge,endnode);
end
end
end
end
% Now check every edgelist, if the edgelength is below the minimum length
% check if we should remove it.
if minlength > 0
for n = 1:Nedges
[spurdegree, spurnode] = connectioninfo(n);
if ~isempty(edgelist{n}) && edgelistlength(edgelist{n}) < minlength
% Remove unconnected lists, or lists that are only connected to
% themselves.
if ~Econnections(n) || (Econnections(n)==1 && B(n,n) == 1)
removeedge(n);
% Process edges that are spurs coming from a 3-way junction.
elseif spurdegree == 2
%fprintf('%d is a spur\n',n) %%debug
linkingedges = find(B(n,:));
if length(linkingedges) == 1 % We have a loop with a spur
% coming from the join in the
% loop
% Just remove the spur, leaving the loop intact.
removeedge(n);
else % Check the other edges coming from this point. If any
% are also spurs make sure we remove the shortest one
spurs = n;
len = edgelistlength(edgelist{n});
for i = 1:length(linkingedges)
spurdegree = connectioninfo(linkingedges(i));
if spurdegree
spurs = [spurs linkingedges(i)];
len = [len edgelistlength(edgelist{linkingedges(i)})];
end
end
linkingedges = [linkingedges n];
[mn,i] = min(len);
edge2delete = spurs(i);
[spurdegree, spurnode] = connectioninfo(edge2delete);
nodes2merge = find(A(spurnode,:));
if length(nodes2merge) ~= 2
error('attempt to merge other than 2 nodes');
end
removeedge(edge2delete);
mergenodes(nodes2merge(1),nodes2merge(2))
end
% Look for spurs coming from 4-way junctions that are below the minimum length
elseif spurdegree == 3
removeedge(n); % Just remove it, no subsequent merging needed.
end
end
end
% Final cleanup of any new isolated edges that might have been created by
% removing spurs. An edge is isolated if it has no connections to other
% edges, or is only connected to itself (in a loop).
for n = 1:Nedges
if ~isempty(edgelist{n}) && edgelistlength(edgelist{n}) < minlength
if ~Econnections(n) || (Econnections(n)==1 && B(n,n) == 1)
removeedge(n);
end
end
end
end % if minlength > 0
% Run through the edgelist and extract out the non-empty lists
m = 0;
for n = 1:Nedges
if ~isempty(edgelist{n})
m = m+1;
nedgelist{m} = edgelist{n};
end
end
%----------------------------------------------------------------------
% Internal function to merge 2 edgelists together at the specified nodes and
% perform the necessary updates to the edge adjacency and node adjacency
% matrices and the connection count arrays
function mergenodes(n1,n2)
edge1 = fix((n1+1)/2); % Indices of the edges associated with the nodes
edge2 = fix((n2+1)/2);
% Get indices of nodes at each end of the two edges
s1 = 2*edge1-1; e1 = 2*edge1;
s2 = 2*edge2-1; e2 = 2*edge2;
if edge1==edge2
% We should not get here, but somehow we occasionally do
% fprintf('Nodes %d %d\n',n1,n2) %% debug
% warning('Attempt to merge an edge with itself')
return
end
if ~A(n1,n2)
error('Attempt to merge nodes that are not connected');
end
if mod(n1,2) % node n1 is the start of edge1
flipedge1 = 1; % edge1 will need to be reversed in order to join edge2
else
flipedge1 = 0;
end
if mod(n2,2) % node n2 is the start of edge2
flipedge2 = 0;
else
flipedge2 = 1;
end
% Join edgelists together - with appropriate reordering depending on which
% end is connected to which. The result is stored in edge1
if ~flipedge1 && ~flipedge2
edgelist{edge1} = [edgelist{edge1}; edgelist{edge2}];
A(e1,:) = A(e2,:); A(:,e1) = A(:,e2);
Nconnections(e1) = Nconnections(e2);
elseif ~flipedge1 && flipedge2
edgelist{edge1} = [edgelist{edge1}; flipud(edgelist{edge2})];
A(e1,:) = A(s2,:); A(:,e1) = A(:,s2);
Nconnections(e1) = Nconnections(s2);
elseif flipedge1 && ~flipedge2
edgelist{edge1} = [flipud(edgelist{edge1}); edgelist{edge2}];
A(s1,:) = A(e1,:); A(:,s1) = A(:,e1);
A(e1,:) = A(e2,:); A(:,e1) = A(:,e2);
Nconnections(s1) = Nconnections(e1);
Nconnections(e1) = Nconnections(e2);
elseif flipedge1 && flipedge2
edgelist{edge1} = [flipud(edgelist{edge1}); flipud(edgelist{edge2})];
A(s1,:) = A(e1,:); A(:,s1) = A(:,e1);
A(e1,:) = A(s2,:); A(:,e1) = A(:,s2);
Nconnections(s1) = Nconnections(e1);
Nconnections(e1) = Nconnections(s2);
else
fprintf('merging edges %d and %d\n',edge1, edge2); %%debug
error('We should not have got here - edgelists cannot be merged');
end
% Now correct the edge adjacency matrix to reflect the new arrangement
% The edges that the new edge1 is connected to is all the edges that
% edge1 and edge2 were connected to
B(edge1,:) = B(edge1,:) | B(edge2,:);
B(:,edge1) = B(:,edge1) | B(:,edge2);
B(edge1, edge1) = 0;
% Recompute connection counts because we have shuffled the adjacency matrices
Econnections = sum(B);
Nconnections = sum(A);
removeedge(edge2); % Finally discard edge2
end % end of mergenodes
%--------------------------------------------------------------------
% Function to provide information about the connections at each end of an
% edgelist
%
% [spurdegree, spurnode, startnode, sconns, endnode, econns] = connectioninfo(n)
%
% spurdegree - If this is non-zero it indicates this edgelist is a spur, the
% value is the number of edges this spur is connected to.
% spurnode - If this is a spur spurnode is the index of the node that is
% connected to other edges, 0 otherwise.
% startnode - index of starting node of edgelist.
% endnode - index of end node of edgelist.
% sconns - number of connections to start node.
% econns - number of connections to end node.
function [spurdegree, spurnode, startnode, sconns, endnode, econns] = connectioninfo(n)
if isempty(edgelist{n})
spurdegree = 0; spurnode = 0;
startnode = 0; sconns = 0; endnode = 0; econns = 0;
return
end
startnode = 2*n-1;
endnode = 2*n;
sconns = Nconnections(startnode); % No of connections to start node
econns = Nconnections(endnode); % No of connections to end node
if sconns == 0 && econns >= 1
spurdegree = econns;
spurnode = endnode;
elseif sconns >= 1 && econns == 0
spurdegree = sconns;
spurnode = startnode;
else
spurdegree = 0;
spurnode = 0;
end
end
%--------------------------------------------------------------------
% Function to remove an edgelist and perform the necessary updates to the edge
% adjacency and node adjacency matrices and the connection count arrays
function removeedge(n)
edgelist{n} = [];
Econnections = Econnections - B(n,:);
Econnections(n) = 0;
B(n,:) = 0;
B(:,n) = 0;
nodes2delete = [2*n-1, 2*n];
Nconnections = Nconnections - A(nodes2delete(1),:);
Nconnections = Nconnections - A(nodes2delete(2),:);
A(nodes2delete, :) = 0;
A(:, nodes2delete) = 0;
end
%--------------------------------------------------------------------
% Function to compute the path length of an edgelist
function l = edgelistlength(edgelist)
l = sum(sqrt(sum((edgelist(1:end-1,:)-edgelist(2:end,:)).^2, 2)));
end
%--------------------------------------------------------------------
end % End of cleanedgelists