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 package org.openimaj.image.analysis.algorithm;
32
33 import java.util.ArrayList;
34 import java.util.List;
35
36 import org.openimaj.feature.DoubleFV;
37 import org.openimaj.feature.FeatureVectorProvider;
38 import org.openimaj.feature.MultidimensionalDoubleFV;
39 import org.openimaj.image.FImage;
40 import org.openimaj.image.analyser.ImageAnalyser;
41 import org.openimaj.image.pixel.ConnectedComponent;
42 import org.openimaj.image.processing.edges.CannyEdgeDetector2;
43 import org.openimaj.math.geometry.point.Point2d;
44 import org.openimaj.math.geometry.point.Point2dImpl;
45 import org.openimaj.math.statistics.distribution.Histogram;
46
47
48
49
50
51
52
53
54
55
56 @SuppressWarnings("deprecation")
57 public class EdgeDirectionCoherenceVector
58 implements ImageAnalyser<FImage>, FeatureVectorProvider<DoubleFV>
59 {
60
61
62
63
64
65
66
67
68 public class EdgeDirectionCoherenceHistogram
69 {
70
71 public Histogram coherentHistogram = null;
72
73
74 public Histogram incoherentHistogram = null;
75
76
77
78
79
80
81
82 public DoubleFV asDoubleFV()
83 {
84 final double[] d = new double[coherentHistogram.values.length +
85 incoherentHistogram.values.length];
86 int i = 0;
87 for (final double dd : coherentHistogram.asDoubleVector())
88 d[i++] = dd;
89 for (final double dd : incoherentHistogram.asDoubleVector())
90 d[i++] = dd;
91 return new DoubleFV(d);
92 }
93
94
95
96
97
98
99
100
101 public MultidimensionalDoubleFV asMultidimensionalDoubleFV()
102 {
103 final double[][] d = new double[2][coherentHistogram.values.length];
104 int i = 0;
105 for (final double dd : coherentHistogram.asDoubleVector())
106 d[0][i++] = dd;
107 i = 0;
108 for (final double dd : incoherentHistogram.asDoubleVector())
109 d[1][i++] = dd;
110 return new MultidimensionalDoubleFV(d);
111 }
112 }
113
114
115 private EdgeDirectionCoherenceHistogram coDirHist = null;
116
117
118 private int numberOfDirBins = 72;
119
120
121 private float directionThreshold = 360 / numberOfDirBins;
122
123
124 private ConnectedComponent.ConnectMode mode =
125 ConnectedComponent.ConnectMode.CONNECT_8;
126
127
128
129
130
131
132 private double coherenceFactor = 0.00002;
133
134
135 private CannyEdgeDetector2 cannyEdgeDetector = null;
136
137
138
139
140 public EdgeDirectionCoherenceVector()
141 {
142 cannyEdgeDetector = new CannyEdgeDetector2();
143 }
144
145
146
147
148 public int getNumberOfDirBins()
149 {
150 return numberOfDirBins;
151 }
152
153
154
155
156
157
158
159 public void setNumberOfBins(int nb)
160 {
161 this.numberOfDirBins = nb;
162 this.directionThreshold = 360 / numberOfDirBins;
163 }
164
165
166
167
168 public EdgeDirectionCoherenceHistogram getLastHistogram()
169 {
170 return coDirHist;
171 }
172
173
174
175
176
177
178
179
180 @Override
181 public void analyseImage(FImage image)
182 {
183 final int w = image.getWidth();
184 final int h = image.getHeight();
185
186
187 final FImage edgeImage = image.clone();
188 cannyEdgeDetector.processImage(edgeImage);
189
190 final float[] mags = cannyEdgeDetector.getMagnitude();
191 final float[] dirs = cannyEdgeDetector.getOrientation();
192
193
194 if (mags == null || dirs == null)
195 System.out.println("Canny Edge Detector did not " +
196 "return magnitude or direction.");
197
198
199 final int numberOfBins = numberOfDirBins + 1;
200
201
202 final double[] dirHist = new double[numberOfBins];
203
204
205
206 int nonEdgeCount = 0;
207 for (int y = 0; y < edgeImage.getHeight(); y++)
208 for (int x = 0; x < edgeImage.getWidth(); x++)
209 if (edgeImage.getPixel(x, y) == 0)
210 nonEdgeCount++;
211 dirHist[0] = nonEdgeCount;
212
213
214
215
216 final FImage directionImage = new FImage(w, h);
217 for (int j = 0; j < w * h; j++)
218 {
219 final int x = j % w;
220 final int y = j / w;
221
222 if (edgeImage.getPixel(x, y) > 0)
223 {
224
225
226 final int dirBin = (int) ((dirs[j] + 180) * numberOfDirBins / 360f) % numberOfDirBins;
227 dirHist[dirBin + 1]++;
228
229 final float v = (dirs[j] + 180);
230 directionImage.setPixel(x, y, v);
231 }
232
233 else
234 directionImage.setPixel(x, y, -1f);
235 }
236
237 final int numberOfEdgePix = w * h - nonEdgeCount;
238
239
240 for (int j = 0; j < numberOfDirBins; j++)
241 dirHist[j + 1] /= numberOfEdgePix;
242 dirHist[0] /= w * h;
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263 coDirHist = new EdgeDirectionCoherenceHistogram();
264 coDirHist.coherentHistogram = new Histogram(numberOfDirBins);
265 coDirHist.incoherentHistogram = new Histogram(numberOfDirBins);
266
267
268 final FImage outputImage = new FImage(w, h);
269
270
271 for (int j = 0; j < w * h; j++)
272 {
273 final int x = j % w;
274 final int y = j / w;
275
276
277 final float p = directionImage.getPixel(x, y);
278
279
280
281 if (p != -1)
282 {
283
284 final List<Point2d> v = getConnectedEdges(x, y, w, h, p,
285 numberOfBins, directionImage, dirs, mode);
286
287
288 final int dirBin = (int) ((dirs[j] + 180)
289 * numberOfDirBins / 360f) % numberOfDirBins;
290
291
292 boolean isCoherent = false;
293 if (v.size() > (w * h * coherenceFactor))
294 {
295 for (int k = 0; k < v.size(); k++)
296 {
297 final Point2d pp = v.get(k);
298 outputImage.setPixel(
299 Math.round(pp.getX()),
300 Math.round(pp.getY()),
301 1f);
302 }
303
304 isCoherent = true;
305 }
306
307 if (isCoherent)
308 coDirHist.coherentHistogram.values[dirBin] += v.size();
309 else
310 coDirHist.incoherentHistogram.values[dirBin] += v.size();
311 }
312 }
313
314 image.internalAssign(outputImage);
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 private List<Point2d> getConnectedEdges(int xx, int yy, int w, int h, float p,
342 int numberOfBins, FImage edgeImage, float[] dirs,
343 ConnectedComponent.ConnectMode connectedness)
344 {
345 final List<Point2d> v = new ArrayList<Point2d>();
346
347
348 v.add(new Point2dImpl(xx, yy));
349
350
351
352 edgeImage.setPixel(xx, yy, -1f);
353
354 final float dir = dirs[yy * w + xx];
355 boolean connected = true;
356 int x = xx, y = yy;
357 while (connected)
358 {
359 int nx = x, ny = y;
360
361 switch (connectedness)
362 {
363
364 case CONNECT_4:
365 nx = x + 1;
366 ny = y;
367 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
368 dirs[ny * w + nx] < dir + directionThreshold &&
369 dirs[ny * w + nx] > dir - directionThreshold &&
370 edgeImage.getPixel(nx, ny) != -1)
371 break;
372 nx = x;
373 ny = y + 1;
374 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
375 dirs[ny * w + nx] < dir + directionThreshold &&
376 dirs[ny * w + nx] > dir - directionThreshold &&
377 edgeImage.getPixel(nx, ny) != -1)
378 break;
379 nx = x - 1;
380 ny = y;
381 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
382 dirs[ny * w + nx] < dir + directionThreshold &&
383 dirs[ny * w + nx] > dir - directionThreshold &&
384 edgeImage.getPixel(nx, ny) != -1)
385 break;
386 nx = x;
387 ny = y - 1;
388 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
389 dirs[ny * w + nx] < dir + directionThreshold &&
390 dirs[ny * w + nx] > dir - directionThreshold &&
391 edgeImage.getPixel(nx, ny) != -1)
392 break;
393 nx = x;
394 ny = y;
395 break;
396
397
398 case CONNECT_8:
399 nx = x + 1;
400 ny = y - 1;
401 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
402 dirs[ny * w + nx] < dir + directionThreshold &&
403 dirs[ny * w + nx] > dir - directionThreshold &&
404 edgeImage.getPixel(nx, ny) != -1)
405 break;
406 nx = x + 1;
407 ny = y;
408 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
409 dirs[ny * w + nx] < dir + directionThreshold &&
410 dirs[ny * w + nx] > dir - directionThreshold &&
411 edgeImage.getPixel(nx, ny) != -1)
412 break;
413 nx = x + 1;
414 ny = y + 1;
415 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
416 dirs[ny * w + nx] < dir + directionThreshold &&
417 dirs[ny * w + nx] > dir - directionThreshold &&
418 edgeImage.getPixel(nx, ny) != -1)
419 break;
420 nx = x;
421 ny = y + 1;
422 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
423 dirs[ny * w + nx] < dir + directionThreshold &&
424 dirs[ny * w + nx] > dir - directionThreshold &&
425 edgeImage.getPixel(nx, ny) != -1)
426 break;
427 nx = x - 1;
428 ny = y + 1;
429 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
430 dirs[ny * w + nx] < dir + directionThreshold &&
431 dirs[ny * w + nx] > dir - directionThreshold &&
432 edgeImage.getPixel(nx, ny) != -1)
433 break;
434 nx = x - 1;
435 ny = y;
436 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
437 dirs[ny * w + nx] < dir + directionThreshold &&
438 dirs[ny * w + nx] > dir - directionThreshold &&
439 edgeImage.getPixel(nx, ny) != -1)
440 break;
441 nx = x - 1;
442 ny = y - 1;
443 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
444 dirs[ny * w + nx] < dir + directionThreshold &&
445 dirs[ny * w + nx] > dir - directionThreshold &&
446 edgeImage.getPixel(nx, ny) != -1)
447 break;
448 nx = x;
449 ny = y - 1;
450 if (nx >= 0 && ny >= 0 && nx < w && ny < h &&
451 dirs[ny * w + nx] < dir + directionThreshold &&
452 dirs[ny * w + nx] > dir - directionThreshold &&
453 edgeImage.getPixel(nx, ny) != -1)
454 break;
455 nx = x;
456 ny = y;
457 break;
458 }
459
460 if ((nx >= 0 && nx != x) || (ny >= 0 && ny != y))
461 {
462 v.add(new Point2dImpl(nx, ny));
463 edgeImage.setPixel(nx, ny, -1f);
464 x = nx;
465 y = ny;
466 }
467 else
468 connected = false;
469 }
470 return v;
471 }
472
473
474
475
476
477
478 public EdgeDirectionCoherenceHistogram getHistogram()
479 {
480 return coDirHist;
481 }
482
483
484
485
486
487
488 @Override
489 public DoubleFV getFeatureVector()
490 {
491 return coDirHist.asMultidimensionalDoubleFV();
492 }
493
494
495
496
497
498
499 public CannyEdgeDetector2 getCannyEdgeDetector()
500 {
501 return cannyEdgeDetector;
502 }
503
504
505
506
507
508
509
510
511 public double getCoherenceFactor()
512 {
513 return coherenceFactor;
514 }
515
516
517
518
519
520
521
522
523
524 public void setCoherenceFactor(double coherenceFactor)
525 {
526 this.coherenceFactor = coherenceFactor;
527 }
528 }