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 package org.openimaj.image.processing.edges;
34
35 import gnu.trove.list.array.TFloatArrayList;
36
37 import org.openimaj.citation.annotation.Reference;
38 import org.openimaj.citation.annotation.ReferenceType;
39 import org.openimaj.image.FImage;
40 import org.openimaj.image.processor.SinglebandImageProcessor;
41
42
43
44
45
46
47
48
49
50
51
52 @Reference(
53 author = { "S. M. Smith" },
54 title = "A new class of corner finder",
55 type = ReferenceType.Article,
56 url = "http://users.fmrib.ox.ac.uk/~steve/susan/susan/node4.html",
57 year = "1992",
58 booktitle = "Proc. 3rd British Machine Vision Conference",
59 pages = "139-148")
60 public class SUSANEdgeDetector implements
61 SinglebandImageProcessor<Float, FImage>
62 {
63
64
65
66
67
68
69
70 private enum SUSANDetector
71 {
72
73
74
75 SIMPLE
76 {
77 @Override
78 public FImage process(FImage img)
79 {
80 return SUSANEdgeDetector.simpleSusan(img, threshold, nmax);
81 }
82 },
83
84
85
86 SMOOTH
87 {
88 @Override
89 public FImage process(FImage img)
90 {
91 return SUSANEdgeDetector.smoothSusan(img, threshold, nmax);
92 }
93 },
94
95
96
97 CIRCULAR
98 {
99 @Override
100 public FImage process(FImage img)
101 {
102 return SUSANEdgeDetector.smoothCircularSusan(img, threshold, nmax, radius);
103 }
104 };
105
106 protected double threshold = 0.08;
107 protected double nmax = 9;
108 protected double radius = 3.4;
109
110 public abstract FImage process(FImage img);
111 }
112
113
114 private SUSANDetector susan = SUSANDetector.SIMPLE;
115
116
117
118
119
120 public SUSANEdgeDetector()
121 {
122 this.susan = SUSANDetector.SIMPLE;
123 }
124
125
126
127
128
129
130
131
132
133 public SUSANEdgeDetector(SUSANDetector s, double threshold,
134 double nmax)
135 {
136 this.susan = s;
137 susan.threshold = threshold;
138 susan.nmax = nmax;
139 }
140
141
142
143
144
145
146
147
148
149
150
151 public SUSANEdgeDetector(SUSANDetector s, double threshold,
152 double nmax, double radius)
153 {
154 this.susan = s;
155 susan.threshold = threshold;
156 susan.nmax = nmax;
157 susan.radius = radius;
158 }
159
160
161
162
163
164
165 @Override
166 public void processImage(FImage image)
167 {
168 image.internalAssign(susan.process(image));
169 }
170
171
172
173
174
175
176
177
178
179
180
181
182 public static FImage simpleSusan(FImage img, double thresh, double nmax)
183 {
184 final FImage area = new FImage(img.getWidth(), img.getHeight());
185
186 final double globalThresh = (3.0 * nmax) / 4.0;
187
188 for (int y = 1; y < img.getHeight() - 1; y++)
189 {
190 for (int x = 1; x < img.getWidth() - 1; x++)
191 {
192 double a = 0;
193 for (int x1 = x - 1; x1 < x + 2; x1++)
194 {
195 for (int y1 = y - 1; y1 < y + 2; y1++)
196 {
197 if (Math.abs(img.getPixel(x1, y1) - img.getPixel(x, y)) < thresh)
198 a++;
199 }
200 }
201
202 if (a < globalThresh)
203 area.setPixel(x, y, (float) (globalThresh - a));
204 }
205 }
206
207 return area;
208 }
209
210
211
212
213
214
215
216
217
218
219
220
221 public static FImage smoothSusan(FImage img, double thresh, double nmax)
222 {
223 final FImage area = new FImage(img.getWidth(), img.getHeight());
224
225 final double globalThresh = (3.0 * nmax) / 4.0;
226
227 for (int y = 1; y < img.getHeight() - 1; y++)
228 {
229 for (int x = 1; x < img.getWidth() - 1; x++)
230 {
231 double a = 0;
232 for (int x1 = x - 1; x1 < x + 2; x1++)
233 {
234 for (int y1 = y - 1; y1 < y + 2; y1++)
235 {
236 a += Math.exp(
237 -Math.pow(
238 Math.abs(img.getPixel(x1, y1) -
239 img.getPixel(x, y))
240 / thresh, 6));
241 }
242 }
243
244 if (a < globalThresh)
245 area.setPixel(x, y, (float) (globalThresh - a));
246 }
247 }
248
249 return area;
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 public static FImage smoothCircularSusan(FImage img, double thresh, double nmax, double radius)
266 {
267 final FImage area = new FImage(img.getWidth(), img.getHeight());
268 final double globalThresh = (3.0 * nmax) / 4.0;
269
270 final int r = (int) Math.ceil(radius);
271 for (int y = r; y < img.getHeight() - r; y++)
272 {
273 for (int x = r; x < img.getWidth() - r; x++)
274 {
275 final float[] pixelValues = getPixelsInCircle(x, y, radius, img);
276 double a = 0;
277 for (final float f : pixelValues)
278 a += Math.exp(
279 -Math.pow(
280 Math.abs(f -
281 img.getPixel(x, y))
282 / thresh, 6));
283
284 if (a < globalThresh)
285 area.setPixel(x, y, (float) (globalThresh - a));
286 }
287 }
288
289 return area;
290 }
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306 private static float[] getPixelsInCircle(int cx, int cy, double r, FImage img)
307 {
308 final TFloatArrayList f = new TFloatArrayList();
309 for (int i = (int) Math.ceil(cx - r); i < (int) Math.ceil(cx + r); i++)
310 {
311 final double ri = Math.sqrt(r * r - (i - cx) * (i - cx));
312 for (int j = (int) Math.ceil(cy - ri); j < (int) Math.ceil(cy + ri); j++)
313 {
314 f.add(img.getPixel(i, j));
315 }
316 }
317 return f.toArray();
318 }
319 }