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.dense.gradient.dsift;
031
032import java.io.DataInput;
033import java.io.DataOutput;
034import java.io.IOException;
035import java.io.PrintWriter;
036import java.util.Scanner;
037import java.util.StringTokenizer;
038
039import org.openimaj.feature.ByteFV;
040
041/**
042 * Dense SIFT keypoint with a location and byte feature vector. Also includes
043 * the energy of the feature prior to normalisation in case low-contrast
044 * features need removing.
045 *
046 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
047 *
048 */
049public class ByteDSIFTKeypoint
050                extends
051                        AbstractDSIFTKeypoint<ByteFV, byte[]>
052{
053        static final long serialVersionUID = 12345545L;
054
055        /**
056         * Construct with the default feature vector length for SIFT (128).
057         */
058        public ByteDSIFTKeypoint() {
059                this(DEFAULT_LENGTH);
060        }
061
062        /**
063         * Construct with the given feature vector length.
064         *
065         * @param length
066         *            the length of the feature vector
067         */
068        public ByteDSIFTKeypoint(final int length) {
069                this.descriptor = new byte[length];
070        }
071
072        /**
073         * Construct with the given parameters.
074         *
075         * @param x
076         *            the x-ordinate of the keypoint
077         * @param y
078         *            the y-ordinate of the keypoint
079         * @param descriptor
080         *            the feature vector of the keypoint
081         * @param energy
082         *            the energy of the keypoint
083         */
084        public ByteDSIFTKeypoint(final float x, final float y, final byte[] descriptor, final float energy) {
085                this.x = x;
086                this.y = y;
087                this.descriptor = descriptor;
088                this.energy = energy;
089        }
090
091        /**
092         * Construct with the given parameters. The float version of the descriptor
093         * is converted to bytes by multiplying each bin by 512, clipping to 255 and
094         * then subtracting 128.
095         *
096         * @param x
097         *            the x-ordinate of the keypoint
098         * @param y
099         *            the y-ordinate of the keypoint
100         * @param fdescriptor
101         *            the flaot version of feature vector of the keypoint
102         * @param energy
103         *            the energy of the keypoint
104         */
105        public ByteDSIFTKeypoint(final float x, final float y, final float[] fdescriptor, final float energy) {
106                this.x = x;
107                this.y = y;
108                this.energy = energy;
109                this.descriptor = new byte[fdescriptor.length];
110
111                for (int i = 0; i < descriptor.length; i++) {
112                        final int intval = (int) (512.0 * fdescriptor[i]);
113                        descriptor[i] = (byte) (Math.min(255, intval) - 128);
114                }
115        }
116
117        @Override
118        public ByteFV getFeatureVector() {
119                return new ByteFV(descriptor);
120        }
121
122        @Override
123        public String toString() {
124                return ("ByteDSIFTKeypoint(" + this.x + ", " + this.y + ")");
125        }
126
127        @Override
128        public void writeBinary(DataOutput out) throws IOException {
129                out.writeFloat(x);
130                out.writeFloat(y);
131                out.writeFloat(energy);
132
133                out.write(this.descriptor);
134        }
135
136        @Override
137        public void writeASCII(PrintWriter out) throws IOException {
138                /* Output data for the keypoint. */
139                out.write(x + " " + y + " " + energy + "\n");
140
141                for (int i = 0; i < descriptor.length; i++) {
142                        if (i > 0 && i % 20 == 0)
143                                out.println();
144                        out.print(" " + (descriptor[i] + 128));
145                }
146                out.println();
147        }
148
149        @Override
150        public void readBinary(DataInput in) throws IOException {
151                x = in.readFloat();
152                y = in.readFloat();
153                energy = in.readFloat();
154                in.readFully(descriptor);
155        }
156
157        @Override
158        public void readASCII(Scanner in) throws IOException {
159                x = in.nextFloat();
160                y = in.nextFloat();
161                energy = in.nextFloat();
162
163                int i = 0;
164                while (i < descriptor.length) {
165                        final String line = in.nextLine();
166                        final StringTokenizer st = new StringTokenizer(line);
167
168                        while (st.hasMoreTokens()) {
169                                descriptor[i] = (byte) (Integer.parseInt(st.nextToken()) - 128);
170                                i++;
171                        }
172                }
173        }
174}