001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.tools.localfeature.options; 031 032import java.io.ByteArrayInputStream; 033import java.io.IOException; 034import java.util.ArrayList; 035import java.util.List; 036 037import org.kohsuke.args4j.CmdLineOptionsProvider; 038import org.kohsuke.args4j.Option; 039import org.kohsuke.args4j.ProxyOptionHandler; 040import org.openimaj.feature.local.LocalFeature; 041import org.openimaj.feature.local.LocalFeatureExtractor; 042import org.openimaj.feature.local.list.LocalFeatureList; 043import org.openimaj.image.FImage; 044import org.openimaj.image.Image; 045import org.openimaj.image.ImageUtilities; 046import org.openimaj.image.MBFImage; 047import org.openimaj.image.colour.ColourSpace; 048import org.openimaj.image.colour.Transforms; 049import org.openimaj.image.feature.dense.gradient.dsift.ApproximateDenseSIFT; 050import org.openimaj.image.feature.dense.gradient.dsift.ByteDSIFTKeypoint; 051import org.openimaj.image.feature.dense.gradient.dsift.ColourDenseSIFT; 052import org.openimaj.image.feature.dense.gradient.dsift.DenseSIFT; 053import org.openimaj.image.feature.dense.gradient.dsift.FloatDSIFTKeypoint; 054import org.openimaj.image.feature.dense.gradient.dsift.PyramidDenseSIFT; 055import org.openimaj.image.feature.local.affine.AffineSimulationKeypoint; 056import org.openimaj.image.feature.local.affine.BasicASIFT; 057import org.openimaj.image.feature.local.affine.ColourASIFT; 058import org.openimaj.image.feature.local.engine.DoGColourSIFTEngine; 059import org.openimaj.image.feature.local.engine.DoGSIFTEngine; 060import org.openimaj.image.feature.local.engine.MinMaxDoGSIFTEngine; 061import org.openimaj.image.feature.local.engine.asift.ASIFTEngine; 062import org.openimaj.image.feature.local.engine.asift.ColourASIFTEngine; 063import org.openimaj.image.feature.local.keypoints.Keypoint; 064import org.openimaj.image.feature.local.keypoints.MinMaxKeypoint; 065import org.openimaj.tools.localfeature.options.ColourMode.ColourModeOp; 066import org.openimaj.tools.localfeature.options.ImageTransform.ImageTransformOp; 067 068/** 069 * Types of local feature 070 * 071 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 072 */ 073public enum LocalFeatureMode implements CmdLineOptionsProvider { 074 /** 075 * Difference-of-Gaussian SIFT 076 */ 077 SIFT { 078 @Override 079 public AbstractDoGSIFTModeOp getOptions() { 080 return new SiftMode(SIFT); 081 } 082 }, 083 /** 084 * Min/Max Difference-of-Gaussian SIFT 085 */ 086 MIN_MAX_SIFT { 087 @Override 088 public LocalFeatureModeOp getOptions() { 089 return new MinMaxSiftMode(MIN_MAX_SIFT); 090 } 091 }, 092 /** 093 * Affine simulated Difference-of-Gaussian SIFT (ASIFT). Outputs x, y, 094 * scale, ori + feature 095 */ 096 ASIFT { 097 @Override 098 public LocalFeatureModeOp getOptions() { 099 return new AsiftMode(ASIFT); 100 } 101 }, 102 /** 103 * Enhanced output affine simulated Difference-of-Gaussian SIFT (ASIFT). 104 * Outputs x, y, scale, ori , tilt, theta, simulation index 105 */ 106 ASIFTENRICHED { 107 @Override 108 public LocalFeatureModeOp getOptions() { 109 return new AsiftEnrichedMode(ASIFTENRICHED); 110 } 111 }, 112 /** 113 * Dense SIFT 114 */ 115 DENSE_SIFT { 116 @Override 117 public LocalFeatureModeOp getOptions() { 118 return new DenseSiftMode(DENSE_SIFT); 119 } 120 }, 121 /** 122 * Colour Dense SIFT 123 */ 124 COLOUR_DENSE_SIFT { 125 @Override 126 public LocalFeatureModeOp getOptions() { 127 return new ColourDenseSiftMode(COLOUR_DENSE_SIFT); 128 } 129 }, 130 /** 131 * Dense SIFT in a pyramid 132 */ 133 PYRAMID_DENSE_SIFT { 134 @Override 135 public LocalFeatureModeOp getOptions() { 136 return new PyramidDenseSiftMode(DENSE_SIFT); 137 } 138 }, 139 /** 140 * Dense colour SIFT in a pyramid 141 */ 142 PYRAMID_COLOUR_DENSE_SIFT { 143 @Override 144 public LocalFeatureModeOp getOptions() { 145 return new PyramidColourDenseSiftMode(COLOUR_DENSE_SIFT); 146 } 147 }; 148 149 @Override 150 public abstract LocalFeatureModeOp getOptions(); 151 152 /** 153 * Associated options for each {@link LocalFeatureMode}. 154 * 155 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 156 */ 157 public static abstract class LocalFeatureModeOp 158 implements 159 LocalFeatureExtractor<LocalFeature<?, ?>, MBFImage> 160 { 161 private LocalFeatureMode mode; 162 163 @Option( 164 name = "--image-transform", 165 aliases = "-it", 166 required = false, 167 usage = "Optionally perform a image transform before keypoint calculation", 168 handler = ProxyOptionHandler.class) 169 protected ImageTransform it = ImageTransform.NOTHING; 170 protected ImageTransformOp itOp = ImageTransform.NOTHING.getOptions(); 171 172 /** 173 * Extract features based on the options. 174 * 175 * @param image 176 * the image 177 * @return the features 178 * @throws IOException 179 */ 180 public abstract LocalFeatureList<? extends LocalFeature<?, ?>> extract(byte[] image) throws IOException; 181 182 private LocalFeatureModeOp(LocalFeatureMode mode) { 183 this.mode = mode; 184 } 185 186 /** 187 * @return the name of the mode 188 */ 189 public String name() { 190 return mode.name(); 191 } 192 193 /** 194 * @return the mode 195 */ 196 public LocalFeatureMode getMode() { 197 return mode; 198 } 199 } 200 201 /** 202 * Associated options for things built on a {@link DoGSIFTEngine}. 203 * 204 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 205 */ 206 public static abstract class AbstractDoGSIFTModeOp extends LocalFeatureModeOp { 207 @Option( 208 name = "--colour-mode", 209 aliases = "-cm", 210 required = false, 211 usage = "Optionally perform sift using the colour of the image in some mode", 212 handler = ProxyOptionHandler.class) 213 protected ColourMode cm = ColourMode.INTENSITY; 214 protected ColourModeOp cmOp = (ColourModeOp) ColourMode.INTENSITY.getOptions(); 215 216 @Option( 217 name = "--no-double-size", 218 aliases = "-nds", 219 required = false, 220 usage = "Double the image sizes for the first iteration") 221 protected boolean noDoubleImageSize = false; 222 223 protected AbstractDoGSIFTModeOp(LocalFeatureMode mode) { 224 super(mode); 225 } 226 } 227 228 private static class SiftMode extends AbstractDoGSIFTModeOp { 229 private SiftMode(LocalFeatureMode mode) { 230 super(mode); 231 } 232 233 @Override 234 public LocalFeatureList<Keypoint> extract(byte[] img) throws IOException { 235 return extract(cmOp.process(img)); 236 } 237 238 @Override 239 public Class<? extends LocalFeature<?, ?>> getFeatureClass() { 240 return Keypoint.class; 241 } 242 243 @Override 244 public LocalFeatureList<Keypoint> extractFeature(MBFImage img) { 245 return extract(cmOp.process(img)); 246 } 247 248 private LocalFeatureList<Keypoint> extract(Image<?, ?> image) { 249 LocalFeatureList<Keypoint> keys = null; 250 switch (this.cm) { 251 case SINGLE_COLOUR: 252 case INTENSITY: { 253 final DoGSIFTEngine engine = new DoGSIFTEngine(); 254 engine.getOptions().setDoubleInitialImage(!noDoubleImageSize); 255 image = itOp.transform(image); 256 257 keys = engine.findFeatures((FImage) image); 258 break; 259 } 260 case INTENSITY_COLOUR: { 261 final DoGColourSIFTEngine engine = new DoGColourSIFTEngine(); 262 engine.getOptions().setDoubleInitialImage(!noDoubleImageSize); 263 image = itOp.transform(image); 264 265 keys = engine.findFeatures((MBFImage) image); 266 break; 267 } 268 } 269 return keys; 270 } 271 } 272 273 private static class MinMaxSiftMode extends AbstractDoGSIFTModeOp { 274 private MinMaxSiftMode(LocalFeatureMode mode) { 275 super(mode); 276 } 277 278 @Override 279 public LocalFeatureList<? extends Keypoint> extract(byte[] img) throws IOException { 280 final MinMaxDoGSIFTEngine engine = new MinMaxDoGSIFTEngine(); 281 LocalFeatureList<MinMaxKeypoint> keys = null; 282 switch (this.cm) { 283 case SINGLE_COLOUR: 284 case INTENSITY: 285 keys = engine.findFeatures((FImage) itOp.transform(cmOp.process(img))); 286 break; 287 case INTENSITY_COLOUR: 288 throw new UnsupportedOperationException(); 289 } 290 return keys; 291 } 292 293 @Override 294 public LocalFeatureList<? extends Keypoint> extractFeature(MBFImage img) { 295 img = (MBFImage) this.itOp.transform(img); 296 final MinMaxDoGSIFTEngine engine = new MinMaxDoGSIFTEngine(); 297 LocalFeatureList<MinMaxKeypoint> keys = null; 298 switch (this.cm) { 299 case SINGLE_COLOUR: 300 case INTENSITY: 301 keys = engine.findFeatures((FImage) cmOp.process(img)); 302 break; 303 case INTENSITY_COLOUR: 304 throw new UnsupportedOperationException(); 305 } 306 return keys; 307 } 308 309 @Override 310 public Class<? extends LocalFeature<?, ?>> getFeatureClass() { 311 return MinMaxKeypoint.class; 312 } 313 } 314 315 private static class AsiftMode extends AbstractDoGSIFTModeOp { 316 private AsiftMode(LocalFeatureMode mode) { 317 super(mode); 318 } 319 320 @Option( 321 name = "--n-tilts", 322 required = false, 323 usage = "The number of tilts for the affine simulation") 324 public int ntilts = 5; 325 326 @Override 327 public LocalFeatureList<Keypoint> extract(byte[] image) throws IOException { 328 LocalFeatureList<Keypoint> keys = null; 329 330 switch (this.cm) { 331 case SINGLE_COLOUR: 332 case INTENSITY: 333 final BasicASIFT basic = new BasicASIFT(!noDoubleImageSize); 334 basic.detectFeatures((FImage) itOp.transform(cmOp.process(image)), ntilts); 335 keys = basic.getFeatures(); 336 break; 337 case INTENSITY_COLOUR: 338 final ColourASIFT colour = new ColourASIFT(!noDoubleImageSize); 339 colour.detectFeatures((MBFImage) itOp.transform(cmOp.process(image)), ntilts); 340 } 341 return keys; 342 } 343 344 @Override 345 public LocalFeatureList<Keypoint> extractFeature(MBFImage image) { 346 LocalFeatureList<Keypoint> keys = null; 347 348 switch (this.cm) { 349 case SINGLE_COLOUR: 350 case INTENSITY: 351 final BasicASIFT basic = new BasicASIFT(!noDoubleImageSize); 352 basic.detectFeatures((FImage) itOp.transform(cmOp.process(image)), ntilts); 353 keys = basic.getFeatures(); 354 break; 355 case INTENSITY_COLOUR: 356 final ColourASIFT colour = new ColourASIFT(!noDoubleImageSize); 357 colour.detectFeatures((MBFImage) itOp.transform(cmOp.process(image)), ntilts); 358 } 359 return keys; 360 } 361 362 @Override 363 public Class<? extends LocalFeature<?, ?>> getFeatureClass() { 364 return Keypoint.class; 365 } 366 } 367 368 private static class AsiftEnrichedMode extends AbstractDoGSIFTModeOp { 369 private AsiftEnrichedMode(LocalFeatureMode mode) { 370 super(mode); 371 } 372 373 @Option( 374 name = "--n-tilts", 375 required = false, 376 usage = "The number of tilts for the affine simulation") 377 public int ntilts = 5; 378 379 @Override 380 public LocalFeatureList<AffineSimulationKeypoint> extract(byte[] image) throws IOException { 381 final ASIFTEngine engine = new ASIFTEngine(!noDoubleImageSize, ntilts); 382 LocalFeatureList<AffineSimulationKeypoint> keys = null; 383 switch (this.cm) { 384 case SINGLE_COLOUR: 385 case INTENSITY: 386 FImage img = (FImage) cmOp.process(image); 387 img = (FImage) itOp.transform(img); 388 keys = engine.findFeatures(img); 389 break; 390 case INTENSITY_COLOUR: 391 final ColourASIFTEngine colourengine = new ColourASIFTEngine(!noDoubleImageSize, ntilts); 392 MBFImage colourimg = (MBFImage) cmOp.process(image); 393 colourimg = (MBFImage) itOp.transform(colourimg); 394 keys = colourengine.findFeatures(colourimg); 395 } 396 return keys; 397 } 398 399 @Override 400 public LocalFeatureList<AffineSimulationKeypoint> extractFeature(MBFImage image) { 401 final ASIFTEngine engine = new ASIFTEngine(!noDoubleImageSize, ntilts); 402 LocalFeatureList<AffineSimulationKeypoint> keys = null; 403 switch (this.cm) { 404 case SINGLE_COLOUR: 405 case INTENSITY: 406 FImage img = (FImage) cmOp.process(image); 407 img = (FImage) itOp.transform(img); 408 keys = engine.findFeatures(img); 409 break; 410 case INTENSITY_COLOUR: 411 final ColourASIFTEngine colourengine = new ColourASIFTEngine(!noDoubleImageSize, ntilts); 412 MBFImage colourimg = (MBFImage) cmOp.process(image); 413 colourimg = (MBFImage) itOp.transform(colourimg); 414 keys = colourengine.findFeatures(colourimg); 415 } 416 return keys; 417 } 418 419 @Override 420 public Class<? extends LocalFeature<?, ?>> getFeatureClass() { 421 return AffineSimulationKeypoint.class; 422 } 423 } 424 425 private static abstract class AbstractDenseSiftMode extends LocalFeatureModeOp { 426 @Option( 427 name = "--approximate", 428 aliases = "-ap", 429 required = false, 430 usage = "Enable approximate mode (much faster)") 431 boolean approximate; 432 433 @Option( 434 name = "--step-x", 435 aliases = "-sx", 436 required = false, 437 usage = "Step size of sampling window in x-direction (in pixels)") 438 protected int stepX = 5; 439 440 @Option( 441 name = "--step-y", 442 aliases = "-sy", 443 required = false, 444 usage = "Step size of sampling window in y-direction (in pixels)") 445 protected int stepY = 5; 446 447 @Option( 448 name = "--num-bins-x", 449 aliases = "-nx", 450 required = false, 451 usage = "Number of spatial bins in the X direction") 452 protected int numBinsX = 4; 453 454 @Option( 455 name = "--num-bins-y", 456 aliases = "-ny", 457 required = false, 458 usage = "Number of spatial bins in the Y direction") 459 protected int numBinsY = 4; 460 461 @Option(name = "--num-ori-bins", aliases = "-no", required = false, usage = "The number of orientation bins") 462 protected int numOriBins = 8; 463 464 @Option( 465 name = "--gaussian-window-size", 466 aliases = "-gws", 467 required = false, 468 usage = "Size of the Gaussian window (in relative to of the size of a bin)") 469 protected float gaussianWindowSize = 2f; 470 471 @Option(name = "--clipping-threshold", required = false, usage = "Threshold for clipping the SIFT features") 472 protected float valueThreshold = 0.2f; 473 474 @Option( 475 name = "--contrast-threshold", 476 required = false, 477 usage = "Threshold on the contrast of the returned features (-ve values disable this)") 478 protected float contrastThreshold = -1; 479 480 @Option( 481 name = "--byte-features", 482 required = false, 483 usage = "Output features scaled to bytes rather than floats") 484 protected boolean byteFeatures = false; 485 486 private AbstractDenseSiftMode(LocalFeatureMode mode) { 487 super(mode); 488 } 489 } 490 491 private static class DenseSiftMode extends AbstractDenseSiftMode { 492 @Option( 493 name = "--bin-width", 494 aliases = "-bw", 495 required = false, 496 usage = "Width of a single bin of the sampling window (in pixels). Sampling window width is this multiplied by #numBinX.") 497 protected int binWidth = 5; 498 499 @Option( 500 name = "--bin-height", 501 aliases = "-bh", 502 required = false, 503 usage = "Height of a single bin of the sampling window (in pixels). Sampling window height is this multiplied by #numBinY.") 504 protected int binHeight = 5; 505 506 private DenseSiftMode(LocalFeatureMode mode) { 507 super(mode); 508 } 509 510 @Override 511 public LocalFeatureList<? extends LocalFeature<?, ?>> extract(byte[] image) throws IOException { 512 return extract(ImageUtilities.readF(new ByteArrayInputStream(image))); 513 } 514 515 @Override 516 public LocalFeatureList<? extends LocalFeature<?, ?>> extractFeature(MBFImage image) { 517 return extract(Transforms.calculateIntensityNTSC_LUT(image)); 518 } 519 520 LocalFeatureList<? extends LocalFeature<?, ?>> extract(FImage image) { 521 image = (FImage) this.itOp.transform(image); 522 523 final DenseSIFT dsift; 524 525 if (approximate) 526 dsift = new ApproximateDenseSIFT(stepX, stepY, binWidth, binHeight, numBinsX, numBinsY, numOriBins, 527 gaussianWindowSize, valueThreshold); 528 else 529 dsift = new DenseSIFT(stepX, stepY, binWidth, binHeight, numBinsX, numBinsY, numOriBins, 530 gaussianWindowSize, valueThreshold); 531 532 dsift.analyseImage(image); 533 534 if (contrastThreshold <= 0) { 535 if (byteFeatures) 536 return dsift.getByteKeypoints(); 537 return dsift.getFloatKeypoints(); 538 } else { 539 if (byteFeatures) 540 return dsift.getByteKeypoints(contrastThreshold); 541 return dsift.getFloatKeypoints(contrastThreshold); 542 } 543 } 544 545 @Override 546 public Class<? extends LocalFeature<?, ?>> getFeatureClass() { 547 if (byteFeatures) 548 return ByteDSIFTKeypoint.class; 549 return FloatDSIFTKeypoint.class; 550 } 551 } 552 553 private static class ColourDenseSiftMode extends DenseSiftMode { 554 @Option(name = "--colour-space", aliases = "-cs", required = false, usage = "Specify the colour space") 555 private ColourSpace colourspace = ColourSpace.RGB; 556 557 ColourDenseSiftMode(LocalFeatureMode mode) { 558 super(mode); 559 } 560 561 @Override 562 public LocalFeatureList<? extends LocalFeature<?, ?>> extract(byte[] image) throws IOException { 563 return extractFeature(ImageUtilities.readMBF(new ByteArrayInputStream(image))); 564 } 565 566 @Override 567 public LocalFeatureList<? extends LocalFeature<?, ?>> extractFeature(MBFImage image) { 568 image = (MBFImage) this.itOp.transform(image); 569 570 final ColourDenseSIFT dsift; 571 572 if (approximate) 573 dsift = new ColourDenseSIFT(new ApproximateDenseSIFT(stepX, stepY, binWidth, binHeight, numBinsX, 574 numBinsY, numOriBins, 575 gaussianWindowSize, valueThreshold), colourspace); 576 else 577 dsift = new ColourDenseSIFT(new DenseSIFT(stepX, stepY, binWidth, binHeight, numBinsX, numBinsY, 578 numOriBins, 579 gaussianWindowSize, valueThreshold), colourspace); 580 581 dsift.analyseImage(image); 582 583 if (contrastThreshold <= 0) { 584 if (byteFeatures) 585 return dsift.getByteKeypoints(); 586 return dsift.getFloatKeypoints(); 587 } else { 588 if (byteFeatures) 589 return dsift.getByteKeypoints(contrastThreshold); 590 return dsift.getFloatKeypoints(contrastThreshold); 591 } 592 } 593 } 594 595 private static class PyramidDenseSiftMode extends AbstractDenseSiftMode { 596 @Option( 597 name = "--sizes", 598 aliases = "-s", 599 required = true, 600 usage = "Scales at which the dense SIFT features are extracted. Each value is used as bin size for the DenseSIFT.") 601 List<Integer> sizes = new ArrayList<Integer>(); 602 603 @Option( 604 name = "--magnification-factor", 605 aliases = "-mf", 606 usage = "The amount to smooth the image by at each level relative to the bin size (sigma = size/magnification).") 607 float magnificationFactor = 6; 608 609 PyramidDenseSiftMode(LocalFeatureMode mode) { 610 super(mode); 611 } 612 613 @Override 614 public LocalFeatureList<? extends LocalFeature<?, ?>> extract(byte[] image) throws IOException { 615 return extractFeature(ImageUtilities.readF(new ByteArrayInputStream(image))); 616 } 617 618 @Override 619 public LocalFeatureList<? extends LocalFeature<?, ?>> extractFeature(MBFImage image) { 620 return extractFeature(Transforms.calculateIntensityNTSC_LUT(image)); 621 } 622 623 protected int[] toArray(List<Integer> in) { 624 final int[] out = new int[in.size()]; 625 626 for (int i = 0; i < out.length; i++) { 627 out[i] = in.get(i); 628 } 629 630 return out; 631 } 632 633 LocalFeatureList<? extends LocalFeature<?, ?>> extractFeature(FImage image) { 634 image = (FImage) this.itOp.transform(image); 635 636 final PyramidDenseSIFT<FImage> dsift; 637 638 if (approximate) 639 dsift = new PyramidDenseSIFT<FImage>(new ApproximateDenseSIFT(stepX, stepY, 1, 1, numBinsX, numBinsY, 640 numOriBins, 641 gaussianWindowSize, valueThreshold), magnificationFactor, toArray(sizes)); 642 else 643 dsift = new PyramidDenseSIFT<FImage>(new DenseSIFT(stepX, stepY, 1, 1, numBinsX, numBinsY, numOriBins, 644 gaussianWindowSize, valueThreshold), magnificationFactor, toArray(sizes)); 645 646 dsift.analyseImage(image); 647 648 if (contrastThreshold <= 0) { 649 if (byteFeatures) 650 return dsift.getByteKeypoints(); 651 return dsift.getFloatKeypoints(); 652 } else { 653 if (byteFeatures) 654 return dsift.getByteKeypoints(contrastThreshold); 655 return dsift.getFloatKeypoints(contrastThreshold); 656 } 657 } 658 659 @Override 660 public Class<? extends LocalFeature<?, ?>> getFeatureClass() { 661 if (byteFeatures) 662 return ByteDSIFTKeypoint.class; 663 return FloatDSIFTKeypoint.class; 664 } 665 } 666 667 private static class PyramidColourDenseSiftMode extends PyramidDenseSiftMode { 668 @Option(name = "--colour-space", aliases = "-cs", required = false, usage = "Specify the colour space") 669 private ColourSpace colourspace = ColourSpace.RGB; 670 671 PyramidColourDenseSiftMode(LocalFeatureMode mode) { 672 super(mode); 673 } 674 675 @Override 676 public LocalFeatureList<? extends LocalFeature<?, ?>> extract(byte[] image) throws IOException { 677 return extractFeature(ImageUtilities.readMBF(new ByteArrayInputStream(image))); 678 } 679 680 @Override 681 public LocalFeatureList<? extends LocalFeature<?, ?>> extractFeature(MBFImage image) { 682 image = (MBFImage) this.itOp.transform(image); 683 684 final PyramidDenseSIFT<MBFImage> dsift; 685 686 if (approximate) 687 dsift = new PyramidDenseSIFT<MBFImage>(new ColourDenseSIFT(new ApproximateDenseSIFT(stepX, stepY, 1, 1, 688 numBinsX, 689 numBinsY, numOriBins, 690 gaussianWindowSize, valueThreshold), colourspace), magnificationFactor, toArray(sizes)); 691 else 692 dsift = new PyramidDenseSIFT<MBFImage>(new ColourDenseSIFT(new DenseSIFT(stepX, stepY, 1, 1, numBinsX, 693 numBinsY, 694 numOriBins, 695 gaussianWindowSize, valueThreshold), colourspace), magnificationFactor, toArray(sizes)); 696 697 dsift.analyseImage(image); 698 699 if (contrastThreshold <= 0) { 700 if (byteFeatures) 701 return dsift.getByteKeypoints(); 702 return dsift.getFloatKeypoints(); 703 } else { 704 if (byteFeatures) 705 return dsift.getByteKeypoints(contrastThreshold); 706 return dsift.getFloatKeypoints(contrastThreshold); 707 } 708 } 709 } 710}