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.image.feature.local.descriptor.gradient;
031
032import org.openimaj.citation.annotation.Reference;
033import org.openimaj.citation.annotation.ReferenceType;
034import org.openimaj.feature.OrientedFeatureVector;
035import org.openimaj.util.array.ArrayUtils;
036
037/**
038 * Irregular binning SIFT descriptor based on this paper:
039 * {@link <a href="http://www.mpi-inf.mpg.de/~hasler/download/CuiHasThoSei09igSIFT.pdf">CuiHasThoSei09igSIFT.pdf</a>}
040 * 
041 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
042 */
043@Reference(
044                type = ReferenceType.Inproceedings,
045                author = { "Cui, Yan", "Hasler, Nils", "Thorm\"{a}hlen, Thorsten", "Seidel, Hans-Peter" },
046                title = "Scale Invariant Feature Transform with Irregular Orientation Histogram Binning",
047                year = "2009",
048                booktitle = "Proceedings of the 6th International Conference on Image Analysis and Recognition",
049                pages = { "258", "", "267" },
050                publisher = "Springer-Verlag",
051                series = "ICIAR '09",
052                customData = {
053                                "Address", "Berlin, Heidelberg"
054        })
055public class IrregularBinningSIFTFeatureProvider implements GradientFeatureProvider, GradientFeatureProviderFactory {
056        private final static float TWO_PI_FLOAT = (float) (Math.PI * 2);
057
058        private final static float FULL_SIZE = 1;
059        private final static float HALF_SIZE = 1 / 2;
060        private final static float QUARTER_SIZE = 1 / 4;
061        private final static float THREE_QUARTER_SIZE = HALF_SIZE + QUARTER_SIZE;
062        private final static float THREE_EIGHTHS_SIZE = 3 / 8;
063        private final static float FIVE_EIGHTHS_SIZE = 5 / 8;
064
065        /** Number of orientation bins in the histograms */
066        protected int numOriBins = 8;
067
068        /** Threshold for the maximum allowed value in the histogram */
069        protected float valueThreshold = 0.2f;
070
071        protected float patchOrientation;
072
073        protected float[] vec;
074
075        /**
076         * Construct a IrregularBinningSIFTFeatureExtractor with the default
077         * parameters.
078         */
079        public IrregularBinningSIFTFeatureProvider() {
080                vec = new float[16 * numOriBins];
081        }
082
083        /**
084         * Construct a IrregularBinningSIFTFeatureExtractor with the default
085         * parameters.
086         * 
087         * @param numOriBins
088         *            the number of orientation bins (default 8)
089         */
090        public IrregularBinningSIFTFeatureProvider(int numOriBins) {
091                this.numOriBins = numOriBins;
092                vec = new float[16 * numOriBins];
093        }
094
095        /**
096         * Construct a IrregularBinningSIFTFeatureExtractor with the default
097         * parameters.
098         * 
099         * @param numOriBins
100         *            the number of orientation bins (default 8)
101         * @param valueThreshold
102         *            threshold for the maximum value allowed in the histogram
103         *            (default 0.2)
104         */
105        public IrregularBinningSIFTFeatureProvider(int numOriBins, float valueThreshold) {
106                this.numOriBins = numOriBins;
107                this.valueThreshold = valueThreshold;
108                vec = new float[16 * numOriBins];
109        }
110
111        @Override
112        public void addSample(float x, float y, float gradmag, float gradori) {
113                // adjust the gradient angle to be relative to the patch angle
114                float ori = gradori - patchOrientation;
115
116                // adjust range to 0<=ori<2PI
117                ori = ((ori %= TWO_PI_FLOAT) >= 0 ? ori : (ori + TWO_PI_FLOAT));
118
119                if (x >= 0 && x < HALF_SIZE && y >= 0 && y < HALF_SIZE)
120                        assignOri(0, 0, ori, gradmag);
121                if (x >= QUARTER_SIZE && x < THREE_QUARTER_SIZE && y >= 0 && y < HALF_SIZE)
122                        assignOri(0, 1, ori, gradmag);
123                if (x >= HALF_SIZE && x < FULL_SIZE && y >= 0 && y < HALF_SIZE)
124                        assignOri(0, 2, ori, gradmag);
125
126                if (x >= 0 && x < HALF_SIZE && y >= QUARTER_SIZE && y < THREE_QUARTER_SIZE)
127                        assignOri(0, 3, ori, gradmag);
128                if (x >= HALF_SIZE && x < FULL_SIZE && y >= QUARTER_SIZE && y < THREE_QUARTER_SIZE)
129                        assignOri(1, 0, ori, gradmag);
130
131                if (x >= 0 && x < HALF_SIZE && y >= HALF_SIZE && y < FULL_SIZE)
132                        assignOri(1, 1, ori, gradmag);
133                if (x >= QUARTER_SIZE && x < THREE_QUARTER_SIZE && y >= HALF_SIZE && y < FULL_SIZE)
134                        assignOri(1, 2, ori, gradmag);
135                if (x >= HALF_SIZE && x < FULL_SIZE && y >= HALF_SIZE && y < FULL_SIZE)
136                        assignOri(1, 3, ori, gradmag);
137
138                if (x >= QUARTER_SIZE && x < HALF_SIZE && y >= QUARTER_SIZE && y < HALF_SIZE)
139                        assignOri(2, 0, ori, gradmag);
140                if (x >= HALF_SIZE && x < THREE_QUARTER_SIZE && y >= QUARTER_SIZE && y < HALF_SIZE)
141                        assignOri(2, 1, ori, gradmag);
142
143                if (x >= QUARTER_SIZE && x < HALF_SIZE && y >= HALF_SIZE && y < THREE_QUARTER_SIZE)
144                        assignOri(2, 2, ori, gradmag);
145                if (x >= HALF_SIZE && x < THREE_QUARTER_SIZE && y >= HALF_SIZE && y < THREE_QUARTER_SIZE)
146                        assignOri(2, 3, ori, gradmag);
147
148                if (x >= THREE_EIGHTHS_SIZE && x < HALF_SIZE && y >= THREE_EIGHTHS_SIZE && y < HALF_SIZE)
149                        assignOri(3, 0, ori, gradmag);
150                if (x >= HALF_SIZE && x < FIVE_EIGHTHS_SIZE && y >= THREE_EIGHTHS_SIZE && y < HALF_SIZE)
151                        assignOri(3, 1, ori, gradmag);
152                if (x >= THREE_EIGHTHS_SIZE && x < HALF_SIZE && y >= HALF_SIZE && y < FIVE_EIGHTHS_SIZE)
153                        assignOri(3, 2, ori, gradmag);
154                if (x >= HALF_SIZE && x < FIVE_EIGHTHS_SIZE && y >= HALF_SIZE && y < FIVE_EIGHTHS_SIZE)
155                        assignOri(3, 3, ori, gradmag);
156        }
157
158        protected void assignOri(int r, int c, float orif, float mag) {
159                final float oval = (float) (numOriBins * orif / (2 * Math.PI));
160                final int oi = (int) ((oval >= 0.0f) ? oval : oval - 1.0f);
161
162                final float ofrac = oval - oi;
163
164                for (int or = 0; or < 2; or++) {
165                        int oindex = oi + or;
166                        if (oindex >= numOriBins) // Orientation wraps at 2PI.
167                                oindex = 0;
168                        final float oweight = mag * ((or == 0) ? 1.0f - ofrac : ofrac);
169
170                        vec[(4 * numOriBins * r) + (numOriBins * c) + oindex] += oweight;
171                }
172        }
173
174        @Override
175        public OrientedFeatureVector getFeatureVector() {
176                ArrayUtils.normalise(vec);
177
178                boolean changed = false;
179                for (int i = 0; i < vec.length; i++) {
180                        if (vec[i] > valueThreshold) {
181                                vec[i] = valueThreshold;
182                                changed = true;
183                        }
184                }
185
186                if (changed)
187                        ArrayUtils.normalise(vec);
188
189                // Construct the actual feature vector
190                final OrientedFeatureVector fv = new OrientedFeatureVector(vec.length, patchOrientation);
191                for (int i = 0; i < vec.length; i++) {
192                        final int intval = (int) (512.0 * vec[i]);
193
194                        fv.values[i] = (byte) (Math.min(255, intval) - 128);
195                }
196
197                return fv;
198        }
199
200        @Override
201        public void setPatchOrientation(float patchOrientation) {
202                this.patchOrientation = patchOrientation;
203        }
204
205        @Override
206        public GradientFeatureProvider newProvider() {
207                return new IrregularBinningSIFTFeatureProvider(numOriBins, valueThreshold);
208        }
209
210        @Override
211        public float getOversamplingAmount() {
212                // no need to over-sample for this feature
213                return 0;
214        }
215}