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.global;
031
032import org.openimaj.feature.DoubleFV;
033import org.openimaj.feature.FeatureVectorProvider;
034import org.openimaj.image.FImage;
035import org.openimaj.image.MBFImage;
036import org.openimaj.image.analyser.ImageAnalyser;
037import org.openimaj.image.colour.Transforms;
038import org.openimaj.image.mask.AbstractMaskedObject;
039
040/**
041 * A two-valued summary representing mean hue (in radians) and variance of hue
042 * respectively. Additionally, can produce a classification for black & white
043 * versus colour versus sepia images based on hand-coded (and not well tested)
044 * parameters.
045 * 
046 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
047 * 
048 */
049public class HueStats extends AbstractMaskedObject<FImage>
050                implements
051                        ImageAnalyser<MBFImage>,
052                        FeatureVectorProvider<DoubleFV>
053{
054        double mean_x = 0;
055        double m2_x = 0;
056        double mean_y = 0;
057        double m2_y = 0;
058        int n = 0;
059
060        /**
061         * Construct with no mask set
062         */
063        public HueStats() {
064                super();
065        }
066
067        /**
068         * Construct with a mask.
069         * 
070         * @param mask
071         *            the mask.
072         */
073        public HueStats(FImage mask) {
074                super(mask);
075        }
076
077        @Override
078        public void analyseImage(MBFImage image) {
079                // reset vars in case we're reused
080                mean_x = 0;
081                m2_x = 0;
082                mean_y = 0;
083                m2_y = 0;
084                n = 0;
085
086                final FImage hue = Transforms.calculateHue(image);
087
088                for (int j = 0; j < hue.height; j++) {
089                        for (int i = 0; i < hue.width; i++) {
090                                if (mask != null && mask.pixels[j][i] == 0)
091                                        continue;
092
093                                final double angle = hue.pixels[j][i];
094
095                                final double x = Math.cos(2 * Math.PI * angle);
096                                final double y = Math.sin(2 * Math.PI * angle);
097
098                                n++;
099                                final double delta_x = x - mean_x;
100                                final double delta_y = y - mean_y;
101                                mean_x += delta_x / n;
102                                mean_y += delta_y / n;
103
104                                m2_x += delta_x * (x - mean_x);
105                                m2_y += delta_y * (y - mean_y);
106                        }
107                }
108        }
109
110        /**
111         * Get the mean hue value.
112         * 
113         * @return the mean hue value over all pixels.
114         */
115        public double getMeanHue() {
116                return Math.atan2(mean_y, mean_x);
117        }
118
119        /**
120         * Get the variance in hue value.
121         * 
122         * @return the variance of hue over all pixels.
123         */
124        public double getHueVariance() {
125                final double var_x = m2_x / n;
126                final double var_y = m2_y / n;
127
128                return var_y * var_x;
129        }
130
131        /**
132         * Tonal attributes for images based on the mean hue and variance.
133         * 
134         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
135         * 
136         */
137        public enum ToneAttr {
138                /**
139                 * Sepia toned image.
140                 */
141                SEPIA,
142                /**
143                 * Black and white image.
144                 */
145                BLACK_AND_WHITE,
146                /**
147                 * Colour image
148                 */
149                COLOR;
150
151                /**
152                 * Estimate the tone from the given mean and variance of the hue. This
153                 * is hand-crafted and not well tested. A variance bigger than 5e-4 is
154                 * taken to imply a colour image. If the variance is less than 5e-4 and
155                 * the mean hue is between -0.1 and 0.1 radians, then it is assumed the
156                 * image is back and white. If the variance is less than 5e-4 and the
157                 * mean hue is between -0.6 and 0.8 radians, then it is assumed the
158                 * image is sepia toned.
159                 * 
160                 * @param mean
161                 *            the mean hue
162                 * @param var
163                 *            the variance in hue
164                 * @return the estimated tone
165                 */
166                public static ToneAttr getAttr(double mean, double var) {
167                        if (var < 5e-4) {
168                                if (mean > -0.1 && mean < 0.1)
169                                        return BLACK_AND_WHITE;
170                                if (mean > 0.6 && mean < 0.8)
171                                        return SEPIA;
172                        }
173                        return COLOR;
174                }
175        }
176
177        /**
178         * Estimate the tone of the image.
179         * 
180         * @see ToneAttr#getAttr(double, double)
181         * 
182         * @return the estimated tone
183         */
184        public ToneAttr getTone() {
185                return ToneAttr.getAttr(getMeanHue(), getHueVariance());
186        }
187
188        @Override
189        public DoubleFV getFeatureVector() {
190                return new DoubleFV(new double[] { getMeanHue(), getHueVariance() });
191        }
192}