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 package org.openimaj.image.processing.convolution;
31
32 import org.openimaj.image.FImage;
33 import org.openimaj.image.analyser.ImageAnalyser;
34
35 import net.jafama.FastMath;
36
37
38
39
40
41
42
43
44
45 public class FImageGradients implements ImageAnalyser<FImage> {
46
47
48
49
50
51
52 public enum Mode {
53
54
55
56 Unsigned(-PI_OVER_TWO_FLOAT, PI_OVER_TWO_FLOAT) {
57 @Override
58 void gradientMagnitudesAndOrientations(FImage image, FImage magnitudes, FImage orientations) {
59 FImageGradients.gradientMagnitudesAndUnsignedOrientations(image, magnitudes, orientations);
60 }
61 },
62
63
64
65 Signed(-PI_FLOAT, PI_FLOAT) {
66 @Override
67 void gradientMagnitudesAndOrientations(FImage image, FImage magnitudes, FImage orientations) {
68 FImageGradients.gradientMagnitudesAndOrientations(image, magnitudes, orientations);
69 }
70 };
71
72 private float min;
73 private float max;
74
75 private Mode(float min, float max) {
76 this.min = min;
77 this.max = max;
78 }
79
80 abstract void gradientMagnitudesAndOrientations(FImage image, FImage magnitudes, FImage orientations);
81
82
83
84
85
86
87 public float minAngle() {
88 return min;
89 }
90
91
92
93
94
95
96 public float maxAngle() {
97 return max;
98 }
99 }
100
101 private final static float PI_FLOAT = (float) Math.PI;
102 private final static float PI_OVER_TWO_FLOAT = (float) Math.PI / 2f;
103 private final static float TWO_PI_FLOAT = (float) (Math.PI * 2);
104
105
106
107
108 public FImage magnitudes;
109
110
111
112
113 public FImage orientations;
114
115
116
117
118 public Mode mode;
119
120
121
122
123 public FImageGradients() {
124 this.mode = Mode.Signed;
125 }
126
127
128
129
130
131
132
133 public FImageGradients(Mode mode) {
134 this.mode = mode;
135 }
136
137
138
139
140
141
142
143
144 @Override
145 public void analyseImage(FImage image) {
146 if (magnitudes == null ||
147 magnitudes.height != image.height ||
148 magnitudes.width != image.width)
149 {
150 magnitudes = new FImage(image.width, image.height);
151 orientations = new FImage(image.width, image.height);
152 }
153
154 mode.gradientMagnitudesAndOrientations(image, magnitudes, orientations);
155 }
156
157
158
159
160
161
162
163
164
165 public static FImageGradients getGradientMagnitudesAndOrientations(FImage image) {
166 final FImageGradients go = new FImageGradients();
167 go.analyseImage(image);
168
169 return go;
170 }
171
172
173
174
175
176
177
178
179
180
181
182 public static FImageGradients getGradientMagnitudesAndOrientations(FImage image, Mode mode) {
183 final FImageGradients go = new FImageGradients(mode);
184 go.analyseImage(image);
185
186 return go;
187 }
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205 public static void gradientMagnitudesAndOrientations(FImage image, FImage magnitudes, FImage orientations) {
206
207
208 for (int r = 0; r < image.height; r++) {
209 for (int c = 0; c < image.width; c++) {
210 float xgrad, ygrad;
211
212 if (c == 0)
213 xgrad = 2.0f * (image.pixels[r][c + 1] - image.pixels[r][c]);
214 else if (c == image.width - 1)
215 xgrad = 2.0f * (image.pixels[r][c] - image.pixels[r][c - 1]);
216 else
217 xgrad = image.pixels[r][c + 1] - image.pixels[r][c - 1];
218 if (r == 0)
219 ygrad = 2.0f * (image.pixels[r][c] - image.pixels[r + 1][c]);
220 else if (r == image.height - 1)
221 ygrad = 2.0f * (image.pixels[r - 1][c] - image.pixels[r][c]);
222 else
223 ygrad = image.pixels[r - 1][c] - image.pixels[r + 1][c];
224
225
226
227
228
229
230
231
232
233 magnitudes.pixels[r][c] = (float) Math.sqrt(xgrad * xgrad + ygrad * ygrad);
234 orientations.pixels[r][c] = (float) FastMath.atan2(ygrad, xgrad);
235 }
236 }
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255 public static void gradientMagnitudesAndUnsignedOrientations(FImage image, FImage magnitudes, FImage orientations) {
256
257
258 for (int r = 0; r < image.height; r++) {
259 for (int c = 0; c < image.width; c++) {
260 float xgrad, ygrad;
261
262 if (c == 0)
263 xgrad = 2.0f * (image.pixels[r][c + 1] - image.pixels[r][c]);
264 else if (c == image.width - 1)
265 xgrad = 2.0f * (image.pixels[r][c] - image.pixels[r][c - 1]);
266 else
267 xgrad = image.pixels[r][c + 1] - image.pixels[r][c - 1];
268 if (r == 0)
269 ygrad = 2.0f * (image.pixels[r][c] - image.pixels[r + 1][c]);
270 else if (r == image.height - 1)
271 ygrad = 2.0f * (image.pixels[r - 1][c] - image.pixels[r][c]);
272 else
273 ygrad = image.pixels[r - 1][c] - image.pixels[r + 1][c];
274
275 magnitudes.pixels[r][c] = (float) Math.sqrt(xgrad * xgrad + ygrad * ygrad);
276 if (magnitudes.pixels[r][c] == 0)
277 orientations.pixels[r][c] = 0;
278 else
279 orientations.pixels[r][c] = (float) FastMath.atan(ygrad / xgrad);
280 }
281 }
282 }
283
284
285
286
287
288
289
290
291
292
293
294
295
296 public static void gradientMagnitudesAndQuantisedOrientations(FImage image, FImage[] magnitudes) {
297 final int numOriBins = magnitudes.length;
298
299
300
301 for (int r = 0; r < image.height; r++) {
302 for (int c = 0; c < image.width; c++) {
303 float xgrad, ygrad;
304
305 if (c == 0)
306 xgrad = 2.0f * (image.pixels[r][c + 1] - image.pixels[r][c]);
307 else if (c == image.width - 1)
308 xgrad = 2.0f * (image.pixels[r][c] - image.pixels[r][c - 1]);
309 else
310 xgrad = image.pixels[r][c + 1] - image.pixels[r][c - 1];
311 if (r == 0)
312 ygrad = 2.0f * (image.pixels[r][c] - image.pixels[r + 1][c]);
313 else if (r == image.height - 1)
314 ygrad = 2.0f * (image.pixels[r - 1][c] - image.pixels[r][c]);
315 else
316 ygrad = image.pixels[r - 1][c] - image.pixels[r + 1][c];
317
318
319
320
321 final float mag = (float) Math.sqrt(xgrad * xgrad + ygrad * ygrad);
322 float ori = (float) FastMath.atan2(ygrad, xgrad);
323
324
325 ori = ((ori %= TWO_PI_FLOAT) >= 0 ? ori : (ori + TWO_PI_FLOAT));
326
327 final float po = numOriBins * ori / TWO_PI_FLOAT;
328
329
330 final int oi = (int) Math.floor(po);
331 final float of = po - oi;
332
333
334 for (int i = 0; i < magnitudes.length; i++)
335 magnitudes[i].pixels[r][c] = 0;
336
337
338 magnitudes[oi % numOriBins].pixels[r][c] = (1f - of) * mag;
339 magnitudes[(oi + 1) % numOriBins].pixels[r][c] = of * mag;
340 }
341 }
342 }
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358 public static void gradientMagnitudesAndQuantisedOrientations(FImage image, FImage[] magnitudes, boolean interp,
359 Mode mode)
360 {
361 final int numOriBins = magnitudes.length;
362
363
364
365 for (int r = 0; r < image.height; r++) {
366 for (int c = 0; c < image.width; c++) {
367 float xgrad, ygrad;
368
369 if (c == 0)
370 xgrad = 2.0f * (image.pixels[r][c + 1] - image.pixels[r][c]);
371 else if (c == image.width - 1)
372 xgrad = 2.0f * (image.pixels[r][c] - image.pixels[r][c - 1]);
373 else
374 xgrad = image.pixels[r][c + 1] - image.pixels[r][c - 1];
375 if (r == 0)
376 ygrad = 2.0f * (image.pixels[r][c] - image.pixels[r + 1][c]);
377 else if (r == image.height - 1)
378 ygrad = 2.0f * (image.pixels[r - 1][c] - image.pixels[r][c]);
379 else
380 ygrad = image.pixels[r - 1][c] - image.pixels[r + 1][c];
381
382
383
384
385 final float mag = (float) Math.sqrt(xgrad * xgrad + ygrad * ygrad);
386
387 float po;
388 if (mode == Mode.Unsigned) {
389 final float ori = mag == 0 ? PI_OVER_TWO_FLOAT
390 : (float) FastMath.atan(ygrad / xgrad)
391 + PI_OVER_TWO_FLOAT;
392
393 po = numOriBins * ori / PI_FLOAT;
394 } else {
395 float ori = (float) FastMath.atan2(ygrad, xgrad);
396
397
398 ori = ((ori %= TWO_PI_FLOAT) >= 0 ? ori : (ori + TWO_PI_FLOAT));
399
400 po = numOriBins * ori / TWO_PI_FLOAT;
401
402 }
403
404
405 for (int i = 0; i < magnitudes.length; i++)
406 magnitudes[i].pixels[r][c] = 0;
407
408 int oi = (int) Math.floor(po);
409 final float of = po - oi;
410
411
412 if (interp) {
413 magnitudes[oi % numOriBins].pixels[r][c] = (1f - of) * mag;
414 magnitudes[(oi + 1) % numOriBins].pixels[r][c] = of * mag;
415 } else {
416
417
418
419 if (oi > numOriBins - 1)
420 oi = numOriBins - 1;
421 magnitudes[oi].pixels[r][c] = mag;
422 }
423 }
424 }
425 }
426 }