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}