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) 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 final MinMaxDoGSIFTEngine engine = new MinMaxDoGSIFTEngine(); 296 LocalFeatureList<MinMaxKeypoint> keys = null; 297 switch (this.cm) { 298 case SINGLE_COLOUR: 299 case INTENSITY: 300 keys = engine.findFeatures((FImage) cmOp.process(img)); 301 break; 302 case INTENSITY_COLOUR: 303 throw new UnsupportedOperationException(); 304 } 305 return keys; 306 } 307 308 @Override 309 public Class<? extends LocalFeature<?, ?>> getFeatureClass() { 310 return MinMaxKeypoint.class; 311 } 312 } 313 314 private static class AsiftMode extends AbstractDoGSIFTModeOp { 315 private AsiftMode(LocalFeatureMode mode) { 316 super(mode); 317 } 318 319 @Option( 320 name = "--n-tilts", 321 required = false, 322 usage = "The number of tilts for the affine simulation") 323 public int ntilts = 5; 324 325 @Override 326 public LocalFeatureList<Keypoint> extract(byte[] image) throws IOException { 327 LocalFeatureList<Keypoint> keys = null; 328 329 switch (this.cm) { 330 case SINGLE_COLOUR: 331 case INTENSITY: 332 final BasicASIFT basic = new BasicASIFT(!noDoubleImageSize); 333 basic.detectFeatures((FImage) itOp.transform(cmOp.process(image)), ntilts); 334 keys = basic.getFeatures(); 335 break; 336 case INTENSITY_COLOUR: 337 final ColourASIFT colour = new ColourASIFT(!noDoubleImageSize); 338 colour.detectFeatures((MBFImage) itOp.transform(cmOp.process(image)), ntilts); 339 } 340 return keys; 341 } 342 343 @Override 344 public LocalFeatureList<Keypoint> extractFeature(MBFImage image) { 345 LocalFeatureList<Keypoint> keys = null; 346 347 switch (this.cm) { 348 case SINGLE_COLOUR: 349 case INTENSITY: 350 final BasicASIFT basic = new BasicASIFT(!noDoubleImageSize); 351 basic.detectFeatures((FImage) itOp.transform(cmOp.process(image)), ntilts); 352 keys = basic.getFeatures(); 353 break; 354 case INTENSITY_COLOUR: 355 final ColourASIFT colour = new ColourASIFT(!noDoubleImageSize); 356 colour.detectFeatures((MBFImage) itOp.transform(cmOp.process(image)), ntilts); 357 } 358 return keys; 359 } 360 361 @Override 362 public Class<? extends LocalFeature<?, ?>> getFeatureClass() { 363 return Keypoint.class; 364 } 365 } 366 367 private static class AsiftEnrichedMode extends AbstractDoGSIFTModeOp { 368 private AsiftEnrichedMode(LocalFeatureMode mode) { 369 super(mode); 370 } 371 372 @Option( 373 name = "--n-tilts", 374 required = false, 375 usage = "The number of tilts for the affine simulation") 376 public int ntilts = 5; 377 378 @Override 379 public LocalFeatureList<AffineSimulationKeypoint> extract(byte[] image) throws IOException { 380 final ASIFTEngine engine = new ASIFTEngine(!noDoubleImageSize, ntilts); 381 LocalFeatureList<AffineSimulationKeypoint> keys = null; 382 switch (this.cm) { 383 case SINGLE_COLOUR: 384 case INTENSITY: 385 FImage img = (FImage) cmOp.process(image); 386 img = (FImage) itOp.transform(img); 387 keys = engine.findFeatures(img); 388 break; 389 case INTENSITY_COLOUR: 390 final ColourASIFTEngine colourengine = new ColourASIFTEngine(!noDoubleImageSize, ntilts); 391 MBFImage colourimg = (MBFImage) cmOp.process(image); 392 colourimg = (MBFImage) itOp.transform(colourimg); 393 keys = colourengine.findFeatures(colourimg); 394 } 395 return keys; 396 } 397 398 @Override 399 public LocalFeatureList<AffineSimulationKeypoint> extractFeature(MBFImage image) { 400 final ASIFTEngine engine = new ASIFTEngine(!noDoubleImageSize, ntilts); 401 LocalFeatureList<AffineSimulationKeypoint> keys = null; 402 switch (this.cm) { 403 case SINGLE_COLOUR: 404 case INTENSITY: 405 FImage img = (FImage) cmOp.process(image); 406 img = (FImage) itOp.transform(img); 407 keys = engine.findFeatures(img); 408 break; 409 case INTENSITY_COLOUR: 410 final ColourASIFTEngine colourengine = new ColourASIFTEngine(!noDoubleImageSize, ntilts); 411 MBFImage colourimg = (MBFImage) cmOp.process(image); 412 colourimg = (MBFImage) itOp.transform(colourimg); 413 keys = colourengine.findFeatures(colourimg); 414 } 415 return keys; 416 } 417 418 @Override 419 public Class<? extends LocalFeature<?, ?>> getFeatureClass() { 420 return AffineSimulationKeypoint.class; 421 } 422 } 423 424 private static abstract class AbstractDenseSiftMode extends LocalFeatureModeOp { 425 @Option( 426 name = "--approximate", 427 aliases = "-ap", 428 required = false, 429 usage = "Enable approximate mode (much faster)") 430 boolean approximate; 431 432 @Option( 433 name = "--step-x", 434 aliases = "-sx", 435 required = false, 436 usage = "Step size of sampling window in x-direction (in pixels)") 437 protected int stepX = 5; 438 439 @Option( 440 name = "--step-y", 441 aliases = "-sy", 442 required = false, 443 usage = "Step size of sampling window in y-direction (in pixels)") 444 protected int stepY = 5; 445 446 @Option( 447 name = "--num-bins-x", 448 aliases = "-nx", 449 required = false, 450 usage = "Number of spatial bins in the X direction") 451 protected int numBinsX = 4; 452 453 @Option( 454 name = "--num-bins-y", 455 aliases = "-ny", 456 required = false, 457 usage = "Number of spatial bins in the Y direction") 458 protected int numBinsY = 4; 459 460 @Option(name = "--num-ori-bins", aliases = "-no", required = false, usage = "The number of orientation bins") 461 protected int numOriBins = 8; 462 463 @Option( 464 name = "--gaussian-window-size", 465 aliases = "-gws", 466 required = false, 467 usage = "Size of the Gaussian window (in relative to of the size of a bin)") 468 protected float gaussianWindowSize = 2f; 469 470 @Option(name = "--clipping-threshold", required = false, usage = "Threshold for clipping the SIFT features") 471 protected float valueThreshold = 0.2f; 472 473 @Option( 474 name = "--contrast-threshold", 475 required = false, 476 usage = "Threshold on the contrast of the returned features (-ve values disable this)") 477 protected float contrastThreshold = -1; 478 479 @Option( 480 name = "--byte-features", 481 required = false, 482 usage = "Output features scaled to bytes rather than floats") 483 protected boolean byteFeatures = false; 484 485 private AbstractDenseSiftMode(LocalFeatureMode mode) { 486 super(mode); 487 } 488 } 489 490 private static class DenseSiftMode extends AbstractDenseSiftMode { 491 @Option( 492 name = "--bin-width", 493 aliases = "-bw", 494 required = false, 495 usage = "Width of a single bin of the sampling window (in pixels). Sampling window width is this multiplied by #numBinX.") 496 protected int binWidth = 5; 497 498 @Option( 499 name = "--bin-height", 500 aliases = "-bh", 501 required = false, 502 usage = "Height of a single bin of the sampling window (in pixels). Sampling window height is this multiplied by #numBinY.") 503 protected int binHeight = 5; 504 505 private DenseSiftMode(LocalFeatureMode mode) { 506 super(mode); 507 } 508 509 @Override 510 public LocalFeatureList<? extends LocalFeature<?, ?>> extract(byte[] image) throws IOException { 511 return extract(ImageUtilities.readF(new ByteArrayInputStream(image))); 512 } 513 514 @Override 515 public LocalFeatureList<? extends LocalFeature<?, ?>> extractFeature(MBFImage image) { 516 return extract(Transforms.calculateIntensityNTSC_LUT(image)); 517 } 518 519 LocalFeatureList<? extends LocalFeature<?, ?>> extract(FImage image) { 520 final DenseSIFT dsift; 521 522 if (approximate) 523 dsift = new ApproximateDenseSIFT(stepX, stepY, binWidth, binHeight, numBinsX, numBinsY, numOriBins, 524 gaussianWindowSize, valueThreshold); 525 else 526 dsift = new DenseSIFT(stepX, stepY, binWidth, binHeight, numBinsX, numBinsY, numOriBins, 527 gaussianWindowSize, valueThreshold); 528 529 dsift.analyseImage(image); 530 531 if (contrastThreshold <= 0) { 532 if (byteFeatures) 533 return dsift.getByteKeypoints(); 534 return dsift.getFloatKeypoints(); 535 } else { 536 if (byteFeatures) 537 return dsift.getByteKeypoints(contrastThreshold); 538 return dsift.getFloatKeypoints(contrastThreshold); 539 } 540 } 541 542 @Override 543 public Class<? extends LocalFeature<?, ?>> getFeatureClass() { 544 if (byteFeatures) 545 return ByteDSIFTKeypoint.class; 546 return FloatDSIFTKeypoint.class; 547 } 548 } 549 550 private static class ColourDenseSiftMode extends DenseSiftMode { 551 @Option(name = "--colour-space", aliases = "-cs", required = false, usage = "Specify the colour space") 552 private ColourSpace colourspace = ColourSpace.RGB; 553 554 ColourDenseSiftMode(LocalFeatureMode mode) { 555 super(mode); 556 } 557 558 @Override 559 public LocalFeatureList<? extends LocalFeature<?, ?>> extract(byte[] image) throws IOException { 560 return extractFeature(ImageUtilities.readMBF(new ByteArrayInputStream(image))); 561 } 562 563 @Override 564 public LocalFeatureList<? extends LocalFeature<?, ?>> extractFeature(MBFImage image) { 565 final ColourDenseSIFT dsift; 566 567 if (approximate) 568 dsift = new ColourDenseSIFT(new ApproximateDenseSIFT(stepX, stepY, binWidth, binHeight, numBinsX, 569 numBinsY, numOriBins, 570 gaussianWindowSize, valueThreshold), colourspace); 571 else 572 dsift = new ColourDenseSIFT(new DenseSIFT(stepX, stepY, binWidth, binHeight, numBinsX, numBinsY, 573 numOriBins, 574 gaussianWindowSize, valueThreshold), colourspace); 575 576 dsift.analyseImage(image); 577 578 if (contrastThreshold <= 0) { 579 if (byteFeatures) 580 return dsift.getByteKeypoints(); 581 return dsift.getFloatKeypoints(); 582 } else { 583 if (byteFeatures) 584 return dsift.getByteKeypoints(contrastThreshold); 585 return dsift.getFloatKeypoints(contrastThreshold); 586 } 587 } 588 } 589 590 private static class PyramidDenseSiftMode extends AbstractDenseSiftMode { 591 @Option( 592 name = "--sizes", 593 aliases = "-s", 594 required = true, 595 usage = "Scales at which the dense SIFT features are extracted. Each value is used as bin size for the DenseSIFT.") 596 List<Integer> sizes = new ArrayList<Integer>(); 597 598 @Option( 599 name = "--magnification-factor", 600 aliases = "-mf", 601 usage = "The amount to smooth the image by at each level relative to the bin size (sigma = size/magnification).") 602 float magnificationFactor = 6; 603 604 PyramidDenseSiftMode(LocalFeatureMode mode) { 605 super(mode); 606 } 607 608 @Override 609 public LocalFeatureList<? extends LocalFeature<?, ?>> extract(byte[] image) throws IOException { 610 return extractFeature(ImageUtilities.readF(new ByteArrayInputStream(image))); 611 } 612 613 @Override 614 public LocalFeatureList<? extends LocalFeature<?, ?>> extractFeature(MBFImage image) { 615 return extractFeature(Transforms.calculateIntensityNTSC_LUT(image)); 616 } 617 618 protected int[] toArray(List<Integer> in) { 619 final int[] out = new int[in.size()]; 620 621 for (int i = 0; i < out.length; i++) { 622 out[i] = in.get(i); 623 } 624 625 return out; 626 } 627 628 LocalFeatureList<? extends LocalFeature<?, ?>> extractFeature(FImage image) { 629 final PyramidDenseSIFT<FImage> dsift; 630 631 if (approximate) 632 dsift = new PyramidDenseSIFT<FImage>(new ApproximateDenseSIFT(stepX, stepY, 1, 1, numBinsX, numBinsY, 633 numOriBins, 634 gaussianWindowSize, valueThreshold), magnificationFactor, toArray(sizes)); 635 else 636 dsift = new PyramidDenseSIFT<FImage>(new DenseSIFT(stepX, stepY, 1, 1, numBinsX, numBinsY, numOriBins, 637 gaussianWindowSize, valueThreshold), magnificationFactor, toArray(sizes)); 638 639 dsift.analyseImage(image); 640 641 if (contrastThreshold <= 0) { 642 if (byteFeatures) 643 return dsift.getByteKeypoints(); 644 return dsift.getFloatKeypoints(); 645 } else { 646 if (byteFeatures) 647 return dsift.getByteKeypoints(contrastThreshold); 648 return dsift.getFloatKeypoints(contrastThreshold); 649 } 650 } 651 652 @Override 653 public Class<? extends LocalFeature<?, ?>> getFeatureClass() { 654 if (byteFeatures) 655 return ByteDSIFTKeypoint.class; 656 return FloatDSIFTKeypoint.class; 657 } 658 } 659 660 private static class PyramidColourDenseSiftMode extends PyramidDenseSiftMode { 661 @Option(name = "--colour-space", aliases = "-cs", required = false, usage = "Specify the colour space") 662 private ColourSpace colourspace = ColourSpace.RGB; 663 664 PyramidColourDenseSiftMode(LocalFeatureMode mode) { 665 super(mode); 666 } 667 668 @Override 669 public LocalFeatureList<? extends LocalFeature<?, ?>> extract(byte[] image) throws IOException { 670 return extractFeature(ImageUtilities.readMBF(new ByteArrayInputStream(image))); 671 } 672 673 @Override 674 public LocalFeatureList<? extends LocalFeature<?, ?>> extractFeature(MBFImage image) { 675 final PyramidDenseSIFT<MBFImage> dsift; 676 677 if (approximate) 678 dsift = new PyramidDenseSIFT<MBFImage>(new ColourDenseSIFT(new ApproximateDenseSIFT(stepX, stepY, 1, 1, 679 numBinsX, 680 numBinsY, numOriBins, 681 gaussianWindowSize, valueThreshold), colourspace), magnificationFactor, toArray(sizes)); 682 else 683 dsift = new PyramidDenseSIFT<MBFImage>(new ColourDenseSIFT(new DenseSIFT(stepX, stepY, 1, 1, numBinsX, 684 numBinsY, 685 numOriBins, 686 gaussianWindowSize, valueThreshold), colourspace), magnificationFactor, toArray(sizes)); 687 688 dsift.analyseImage(image); 689 690 if (contrastThreshold <= 0) { 691 if (byteFeatures) 692 return dsift.getByteKeypoints(); 693 return dsift.getFloatKeypoints(); 694 } else { 695 if (byteFeatures) 696 return dsift.getByteKeypoints(contrastThreshold); 697 return dsift.getFloatKeypoints(contrastThreshold); 698 } 699 } 700 } 701}