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.feature.dense.gradient.dsift;
31
32 import org.openimaj.feature.local.list.LocalFeatureList;
33 import org.openimaj.feature.local.list.MemoryLocalFeatureList;
34 import org.openimaj.image.FImage;
35 import org.openimaj.image.processing.convolution.FImageConvolveSeparable;
36 import org.openimaj.image.processing.convolution.FImageGradients;
37 import org.openimaj.math.geometry.shape.Rectangle;
38 import org.openimaj.util.array.ArrayUtils;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class DenseSIFT extends AbstractDenseSIFT<FImage> {
61 static class WorkingData {
62
63
64
65 protected int boundMinX;
66
67
68
69
70 protected int boundMaxX;
71
72
73
74
75 protected int boundMinY;
76
77
78
79
80 protected int boundMaxY;
81
82
83
84
85
86
87
88 protected FImage[] gradientMagnitudes;
89
90
91
92
93
94
95
96 protected void setupWorkingSpace(FImage image, DenseSIFT dsift) {
97 if (gradientMagnitudes == null) {
98 gradientMagnitudes = new FImage[dsift.numOriBins];
99 }
100
101 if (gradientMagnitudes[0] == null || gradientMagnitudes[0].width != image.width
102 || gradientMagnitudes[0].height != image.height)
103 {
104 for (int i = 0; i < dsift.numOriBins; i++)
105 gradientMagnitudes[i] = new FImage(image.width, image.height);
106 }
107
108 final int rangeX = boundMaxX - boundMinX - (dsift.numBinsX - 1) * dsift.binWidth;
109 final int rangeY = boundMaxY - boundMinY - (dsift.numBinsY - 1) * dsift.binHeight;
110
111 final int numWindowsX = (rangeX >= 0) ? rangeX / dsift.stepX + 1 : 0;
112 final int numWindowsY = (rangeY >= 0) ? rangeY / dsift.stepY + 1 : 0;
113
114 final int numFeatures = numWindowsX * numWindowsY;
115
116 dsift.descriptors = new float[numFeatures][dsift.numOriBins * dsift.numBinsX * dsift.numBinsY];
117 dsift.energies = new float[numFeatures];
118 }
119 }
120
121
122
123
124 protected int stepX = 5;
125
126
127
128
129 protected int stepY = 5;
130
131
132
133
134
135 protected int binWidth = 5;
136
137
138
139
140
141 protected int binHeight = 5;
142
143
144
145
146 protected int numBinsX = 4;
147
148
149
150
151 protected int numBinsY = 4;
152
153
154 protected int numOriBins = 8;
155
156
157
158
159 protected float gaussianWindowSize = 2f;
160
161
162
163
164 protected float valueThreshold = 0.2f;
165
166 protected volatile WorkingData data = new WorkingData();
167
168
169
170
171 protected volatile float[][] descriptors;
172
173
174
175
176 protected volatile float[] energies;
177
178
179
180
181
182
183 public DenseSIFT() {
184 }
185
186
187
188
189
190
191
192
193
194
195 public DenseSIFT(int step, int binSize) {
196 this.binWidth = binSize;
197 this.binHeight = binSize;
198 this.stepX = step;
199 this.stepY = step;
200 }
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221 public DenseSIFT(int stepX, int stepY, int binWidth, int binHeight, int numBinsX, int numBinsY, int numOriBins) {
222 this(stepX, stepY, binWidth, binHeight, numBinsX, numBinsY, numOriBins, 2f);
223 }
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 public DenseSIFT(int stepX, int stepY, int binWidth, int binHeight, int numBinsX, int numBinsY, int numOriBins,
247 float gaussianWindowSize)
248 {
249 this(stepX, stepY, binWidth, binHeight, numBinsX, numBinsY, numOriBins, gaussianWindowSize, 0.2f);
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 public DenseSIFT(int stepX, int stepY, int binWidth, int binHeight, int numBinsX, int numBinsY, int numOriBins,
275 float gaussianWindowSize, float valueThreshold)
276 {
277 this.binWidth = binWidth;
278 this.binHeight = binHeight;
279 this.stepX = stepX;
280 this.stepY = stepY;
281 this.numBinsX = numBinsX;
282 this.numBinsY = numBinsY;
283 this.numOriBins = numOriBins;
284 this.gaussianWindowSize = gaussianWindowSize;
285 this.valueThreshold = valueThreshold;
286 }
287
288 private float[] buildKernel(int binSize, int numBins, int binIndex, float windowSize) {
289 final int kernelSize = 2 * binSize - 1;
290 final float[] kernel = new float[kernelSize];
291 final float delta = binSize * (binIndex - 0.5F * (numBins - 1));
292
293 final float sigma = binSize * windowSize;
294
295 for (int x = -binSize + 1, i = 0; x <= +binSize - 1; x++, i++) {
296 final float z = (x - delta) / sigma;
297
298 kernel[i] = (1.0F - Math.abs(x) / binSize) * ((binIndex >= 0) ? (float) Math.exp(-0.5F * z * z) : 1.0F);
299 }
300
301 return kernel;
302 }
303
304
305
306
307 protected void extractFeatures() {
308 final int frameSizeX = binWidth * (numBinsX - 1) + 1;
309 final int frameSizeY = binHeight * (numBinsY - 1) + 1;
310
311 for (int biny = 0; biny < numBinsY; biny++) {
312 final float[] yker = buildKernel(binHeight, numBinsY, biny, gaussianWindowSize);
313
314 for (int binx = 0; binx < numBinsX; binx++) {
315 final float[] xker = buildKernel(binWidth, numBinsX, binx, gaussianWindowSize);
316
317 for (int bint = 0; bint < numOriBins; bint++) {
318 final FImage conv = data.gradientMagnitudes[bint].process(new FImageConvolveSeparable(xker, yker));
319 final float[][] src = conv.pixels;
320
321 final int descriptorOffset = bint + binx * numOriBins + biny * (numBinsX * numOriBins);
322 int descriptorIndex = 0;
323
324 for (int framey = data.boundMinY; framey <= data.boundMaxY - frameSizeY + 1; framey += stepY) {
325 for (int framex = data.boundMinX; framex <= data.boundMaxX - frameSizeX + 1; framex += stepX) {
326 descriptors[descriptorIndex][descriptorOffset] = src[framey + biny * binHeight][framex
327 + binx * binWidth];
328 descriptorIndex++;
329 }
330 }
331 }
332 }
333 }
334 }
335
336 @Override
337 public void analyseImage(FImage image, Rectangle bounds) {
338 if (data == null)
339 data = new WorkingData();
340
341 data.boundMinX = (int) bounds.x;
342 data.boundMaxX = (int) (bounds.width - 1);
343 data.boundMinY = (int) bounds.y;
344 data.boundMaxY = (int) (bounds.height - 1);
345
346 data.setupWorkingSpace(image, this);
347
348 FImageGradients.gradientMagnitudesAndQuantisedOrientations(image, data.gradientMagnitudes);
349
350 extractFeatures();
351
352 normaliseDescriptors();
353 }
354
355 private void normaliseDescriptors() {
356 final int frameSizeX = binWidth * (numBinsX - 1) + 1;
357 final int frameSizeY = binHeight * (numBinsY - 1) + 1;
358 final float energyNorm = frameSizeX * frameSizeY;
359
360 for (int j = 0; j < descriptors.length; j++) {
361 final float[] arr = descriptors[j];
362
363 energies[j] = ArrayUtils.sumValues(arr) / energyNorm;
364
365 ArrayUtils.normalise(arr);
366
367 boolean changed = false;
368 for (int i = 0; i < arr.length; i++) {
369 if (arr[i] > valueThreshold) {
370 arr[i] = valueThreshold;
371 changed = true;
372 }
373 }
374
375 if (changed)
376 ArrayUtils.normalise(arr);
377 }
378 }
379
380 @Override
381 public LocalFeatureList<FloatDSIFTKeypoint> getFloatKeypoints() {
382 final MemoryLocalFeatureList<FloatDSIFTKeypoint> keys = new MemoryLocalFeatureList<FloatDSIFTKeypoint>(numOriBins
383 * numBinsX * numBinsY, descriptors.length);
384
385 final int frameSizeX = binWidth * (numBinsX - 1) + 1;
386 final int frameSizeY = binHeight * (numBinsY - 1) + 1;
387
388 final float deltaCenterX = 0.5F * binWidth * (numBinsX - 1);
389 final float deltaCenterY = 0.5F * binHeight * (numBinsY - 1);
390
391 for (int framey = data.boundMinY, i = 0; framey <= data.boundMaxY - frameSizeY + 1; framey += stepY) {
392 for (int framex = data.boundMinX; framex <= data.boundMaxX - frameSizeX + 1; framex += stepX, i++) {
393 keys.add(new FloatDSIFTKeypoint(framex + deltaCenterX, framey + deltaCenterY, descriptors[i],
394 energies[i]));
395 }
396 }
397
398 return keys;
399 }
400
401 @Override
402 public LocalFeatureList<ByteDSIFTKeypoint> getByteKeypoints() {
403 final MemoryLocalFeatureList<ByteDSIFTKeypoint> keys = new MemoryLocalFeatureList<ByteDSIFTKeypoint>(numOriBins
404 * numBinsX * numBinsY, descriptors.length);
405
406 final int frameSizeX = binWidth * (numBinsX - 1) + 1;
407 final int frameSizeY = binHeight * (numBinsY - 1) + 1;
408
409 final float deltaCenterX = 0.5F * binWidth * (numBinsX - 1);
410 final float deltaCenterY = 0.5F * binHeight * (numBinsY - 1);
411
412 for (int framey = data.boundMinY, i = 0; framey <= data.boundMaxY - frameSizeY + 1; framey += stepY) {
413 for (int framex = data.boundMinX; framex <= data.boundMaxX - frameSizeX + 1; framex += stepX, i++) {
414 keys.add(
415 new ByteDSIFTKeypoint(framex + deltaCenterX, framey + deltaCenterY, descriptors[i], energies[i]));
416 }
417 }
418
419 return keys;
420 }
421
422 @Override
423 public LocalFeatureList<FloatDSIFTKeypoint> getFloatKeypoints(float energyThreshold) {
424 final MemoryLocalFeatureList<FloatDSIFTKeypoint> keys = new MemoryLocalFeatureList<FloatDSIFTKeypoint>(numOriBins
425 * numBinsX * numBinsY);
426
427 final int frameSizeX = binWidth * (numBinsX - 1) + 1;
428 final int frameSizeY = binHeight * (numBinsY - 1) + 1;
429
430 final float deltaCenterX = 0.5F * binWidth * (numBinsX - 1);
431 final float deltaCenterY = 0.5F * binHeight * (numBinsY - 1);
432
433 for (int framey = data.boundMinY, i = 0; framey <= data.boundMaxY - frameSizeY + 1; framey += stepY) {
434 for (int framex = data.boundMinX; framex <= data.boundMaxX - frameSizeX + 1; framex += stepX, i++) {
435 if (energies[i] >= energyThreshold)
436 keys.add(new FloatDSIFTKeypoint(framex + deltaCenterX, framey + deltaCenterY, descriptors[i],
437 energies[i]));
438 }
439 }
440
441 return keys;
442 }
443
444 @Override
445 public LocalFeatureList<ByteDSIFTKeypoint> getByteKeypoints(float energyThreshold) {
446 final MemoryLocalFeatureList<ByteDSIFTKeypoint> keys = new MemoryLocalFeatureList<ByteDSIFTKeypoint>(numOriBins
447 * numBinsX * numBinsY);
448
449 final int frameSizeX = binWidth * (numBinsX - 1) + 1;
450 final int frameSizeY = binHeight * (numBinsY - 1) + 1;
451
452 final float deltaCenterX = 0.5F * binWidth * (numBinsX - 1);
453 final float deltaCenterY = 0.5F * binHeight * (numBinsY - 1);
454
455 for (int framey = data.boundMinY, i = 0; framey <= data.boundMaxY - frameSizeY + 1; framey += stepY) {
456 for (int framex = data.boundMinX; framex <= data.boundMaxX - frameSizeX + 1; framex += stepX, i++) {
457 if (energies[i] >= energyThreshold)
458 keys.add(new ByteDSIFTKeypoint(framex + deltaCenterX, framey + deltaCenterY, descriptors[i],
459 energies[i]));
460 }
461 }
462
463 return keys;
464 }
465
466
467
468
469
470
471
472
473 @Override
474 public float[][] getDescriptors() {
475 return descriptors;
476 }
477
478 @Override
479 public DenseSIFT clone() {
480 final DenseSIFT clone = (DenseSIFT) super.clone();
481
482 clone.descriptors = null;
483 clone.energies = null;
484 clone.data = null;
485
486 return clone;
487 }
488
489 @Override
490 public void setBinWidth(int size) {
491 this.binWidth = size;
492 }
493
494 @Override
495 public void setBinHeight(int size) {
496 this.binHeight = size;
497 }
498
499 @Override
500 public int getBinWidth() {
501 return binWidth;
502 }
503
504 @Override
505 public int getBinHeight() {
506 return binHeight;
507 }
508
509 @Override
510 public int getNumBinsX() {
511 return numBinsX;
512 }
513
514 @Override
515 public int getNumBinsY() {
516 return numBinsY;
517 }
518
519 @Override
520 public int getNumOriBins() {
521 return numOriBins;
522 }
523 }