forked from golanlevin/QR_STENCILER
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ConnectedComponentLabeler.pde
263 lines (221 loc) · 8.2 KB
/
ConnectedComponentLabeler.pde
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
// Author: "Golan Levin" <[email protected]>, 01 August, 2011
//================================================================
class ConnectedComponentLabeler {
// Adapted from:
// http://courses.csail.mit.edu/6.141/spring2010/pub/labs/VisualServo/src/ConnectedComponents.java
// ConnectedComponentLabeler is an algorithm that applies Connected Component Labeling
// algorithm to an input image. Only mono images are catered for.
// author: Neil Brown, DAI
// author: Judy Robertson, SELLIC OnLine
//----------------------------------------------------------------------
//the width of the input image in pixels
int i1_w;
//the width and height of the output image
int d_w;
int d_h;
int numberOfLabels;
boolean labelsValid = false;
int dest_1d[];
int labels[];
int labelColors[];
//----------------------------------------------------------------------
// Constructs a new Image Operator
// param firstwidth The width of the input image
// param firstheight The height of the input image
ConnectedComponentLabeler( int firstwidth, int firstheight ) {
labels = new int[firstwidth * firstheight];
i1_w = firstwidth;
}
//----------------------------------------------------------------------
// getNeighbors will get the pixel value of i's neighbor that's ox and oy
// away from i, if the point is outside the image, then 0 is returned.
// This version gets from source image.
int getNeighbors( int [] src1d, int i, int ox, int oy ) {
int x, y, result;
x = ( i % d_w ) + ox; // d_w and d_h are assumed to be set to the
y = ( i / d_w ) + oy; // width and height of scr1d
if ( ( x < 0 ) || ( x >= d_w ) || ( y < 0 ) || ( y >= d_h ) ) {
result = 0;
}
else {
result = src1d[ y * d_w + x ] & 0x000000ff;
}
return result;
}
//----------------------------------------------------------------------
// getNeighbord will get the pixel value of i's neighbor that's ox and oy
// away from i, if the point is outside the image, then 0 is returned.
// This version gets from destination image.
int getNeighbord( int [] src1d, int i, int ox, int oy ) {
int x, y, result;
x = ( i % d_w ) + ox; // d_w and d_h are assumed to be set to the
y = ( i / d_w ) + oy; // width and height of scr1d
if ( (x < 0) || (x >= d_w) || (y < 0) || (y >= d_h) ) {
result = 0;
}
else {
result = src1d[ y * d_w + x ];
}
return result;
}
//----------------------------------------------------------------------
// Associate(equivalence) a with b.
// a should be less than b to give some ordering (sorting)
// if b is already associated with some other value, then propagate
// down the list.
void associate( int a, int b ) {
if ( a > b ) {
associate( b, a );
return;
}
if ( ( a == b ) || ( labels[ b ] == a ) ) return;
if ( labels[ b ] == b ) {
labels[ b ] = a;
}
else {
associate ( labels[ b ], a );
if (labels[ b ] > a) {
labels[ b ] = a;
}
}
}
//----------------------------------------------------------------------
// Reduces the number of labels.
int reduce( int a ) {
if ( labels[ a ] == a ) {
return a;
}
else {
return reduce( labels[ a ] );
}
}
//----------------------------------------------------------------------
// doLabel applies the Labeling algorithm plus offset and scaling
// The input image is expected to be 8-bit mono, 0=black everything else=white
// param src1_1d The input pixel array
// param width width of the destination image in pixels
// param height height of the destination image in pixels
// return A pixel array containing the labelled image
// NB For images 0,0 is the top left corner.
int [] doLabel(int [] src1_1d, int wid, int hig) {
int nextlabel = 1;
int nbs[] = new int[ 4 ];
int nbls[] = new int[ 4 ];
//Get size of image and make 1d_arrays
d_w = wid;
d_h = hig;
dest_1d = new int[d_w*d_h];
labels = new int[d_w*d_h / 2]; // the most labels there can be is 1/2 of the points in checkerboard
int src1rgb;
int result = 0;
int px, py, count, found;
labelsValid = false; // only set to true once we've complete the task
//initialise labels
for (int i=0; i<labels.length; i++) labels[ i ] = i;
//now Label the image
for (int i=0; i< src1_1d. length; i++) {
src1rgb = src1_1d[ i ] & 0x000000ff;
if ( src1rgb == 0 ) {
result = 0; //nothing here
}
else {
//The 4 visited neighbors
nbs[ 0 ] = getNeighbors( src1_1d, i, -1, 0 );
nbs[ 1 ] = getNeighbors( src1_1d, i, 0, -1 );
nbs[ 2 ] = getNeighbors( src1_1d, i, -1, -1 );
nbs[ 3 ] = getNeighbors( src1_1d, i, 1, -1 );
//Their corresponding labels
nbls[ 0 ] = getNeighbord( dest_1d, i, -1, 0 );
nbls[ 1 ] = getNeighbord( dest_1d, i, 0, -1 );
nbls[ 2 ] = getNeighbord( dest_1d, i, -1, -1 );
nbls[ 3 ] = getNeighbord( dest_1d, i, 1, -1 );
//label the point
if ( (nbs[0] == nbs[1]) && (nbs[1] == nbs[2]) && (nbs[2] == nbs[3])
&& (nbs[0] == 0 )) {
// all neighbors are 0 so gives this point a new label
result = nextlabel;
nextlabel++;
}
else { //one or more neighbors have already got labels
count = 0;
found = -1;
for ( int j=0; j<4; j++) {
if ( nbs[ j ] != 0 ) {
count +=1;
found = j;
}
}
if ( count == 1 ) {
// only one neighbor has a label, so assign the same label to this.
result = nbls[ found ];
}
else {
// more than 1 neighbor has a label
result = nbls[ found ];
// Equivalence the connected points
for ( int j=0; j<4; j++) {
if ( ( nbls[ j ] != 0 ) && (nbls[ j ] != result ) ) {
associate( nbls[ j ], result );
}
}
}
}
}
dest_1d[i] = result;
}
// reduce labels ie 76=23=22=3 -> 76=3
// done in reverse order to preserve sorting
for ( int i= labels.length -1; i > 0; i-- ) {
labels[ i ] = reduce( i );
}
// now labels will look something like 1=1 2=2 3=2 4=2 5=5.. 76=5 77=5
// this needs to be condensed down again, so that there is no wasted
// space. E.g. in the above, the labels 3 and 4 are not used, instead it jumps to 5
int condensed[] = new int[ nextlabel ]; // cant be more than nextlabel labels
count = 0;
for (int i=0; i< nextlabel; i++) {
if ( i == labels[ i ] ) condensed[ i ] = count++;
}
// Record the number of labels
numberOfLabels = count - 1;
// now run back through our preliminary results, replacing the raw label
// with the reduced and condensed one, and do the scaling and offsets too
// Now generate an array of colours which will be used to label the image
labelColors = new int[numberOfLabels+1];
// The likelihood that two colors would be the same... is very small.
for (int i = 0; i < labelColors.length; i++) {
int rr = (int) random(1, 254);
int rg = (int) random(1, 254);
int rb = (int) random(1, 254);
color randomColor = color(rr, rg, rb, 255);
labelColors[i] = randomColor;
if (i == 0) labelColors[i] = black;
if (i == 1) labelColors[i] = white;
}
for (int i=0; i< src1_1d. length; i++) {
result = condensed[ labels[ dest_1d[ i ] ] ];
dest_1d[i] = labelColors[result];
}
labelsValid = true; // only set to true now we've complete the task
return dest_1d;
}
//----------------------------------------------------------------------
//return the number of unique, non zero colours. -1 if not valid
int getColors() {
if ( labelsValid ) {
return numberOfLabels;
}
else {
return -1;
}
}
//----------------------------------------------------------------------
//Returns the number of labels.
int getNumberOfLabels() {
return numberOfLabels;
}
//----------------------------------------------------------------------
int[] getLabelColors() {
return labelColors;
}
}