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.citation.annotation.Reference;
033import org.openimaj.citation.annotation.ReferenceType;
034import org.openimaj.feature.DoubleFV;
035import org.openimaj.feature.FeatureVectorProvider;
036import org.openimaj.image.FImage;
037import org.openimaj.image.MBFImage;
038import org.openimaj.image.analyser.ImageAnalyser;
039import org.openimaj.image.colour.Transforms;
040import org.openimaj.image.mask.AbstractMaskedObject;
041
042/**
043 * Implementation of the Naturalness index (CNI) defined by Huang, Qiao & Wu.
044 * The CNI is a single valued summary of how natural the colours in an image
045 * are. The index is a value between 0 and 1. Higher values indicate that the
046 * colours in the image are more natural .
047 * 
048 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
049 */
050@Reference(
051                type = ReferenceType.Article,
052                author = { "Huang, Kai-Qi", "Wang, Qiao", "Wu, Zhen-Yang" },
053                title = "Natural color image enhancement and evaluation algorithm based on human visual system",
054                year = "2006",
055                journal = "Comput. Vis. Image Underst.",
056                pages = { "52", "", "63" },
057                url = "http://dx.doi.org/10.1016/j.cviu.2006.02.007",
058                month = "jul",
059                number = "1",
060                publisher = "Elsevier Science Inc.",
061                volume = "103")
062public class Naturalness extends AbstractMaskedObject<FImage>
063                implements
064                ImageAnalyser<MBFImage>,
065                FeatureVectorProvider<DoubleFV>
066{
067        private final static double grassLower = 95.0 / 360.0;
068        private final static double grassUpper = 135.0 / 360.0;
069        private final static double skinLower = 25.0 / 360.0;
070        private final static double skinUpper = 70.0 / 360.0;
071        private final static double skyLower = 185.0 / 360.0;
072        private final static double skyUpper = 260.0 / 360.0;
073
074        private final static double satLower = 0.1;
075
076        private final static double lightnessLower = 20.0 / 100.0;
077        private final static double lightnessUpper = 80.0 / 100.0;
078
079        private double skyMean = 0;
080        private int skyN = 0;
081
082        private double skinMean = 0;
083        private int skinN = 0;
084
085        private double grassMean = 0;
086        private int grassN = 0;
087
088        /**
089         * Construct with no mask set
090         */
091        public Naturalness() {
092                super();
093        }
094
095        /**
096         * Construct with a mask.
097         * 
098         * @param mask
099         *            the mask.
100         */
101        public Naturalness(FImage mask) {
102                super(mask);
103        }
104
105        @Override
106        public void analyseImage(MBFImage image) {
107                // reset vars in case we're reused
108                skyMean = 0;
109                skyN = 0;
110                skinMean = 0;
111                skinN = 0;
112                grassMean = 0;
113                grassN = 0;
114
115                final MBFImage hsl = Transforms.RGB_TO_HSL(image);
116
117                final FImage H = (hsl).getBand(0);
118                final FImage S = (hsl).getBand(1);
119                final FImage L = (hsl).getBand(2);
120
121                for (int y = 0; y < H.height; y++) {
122                        for (int x = 0; x < H.width; x++) {
123                                if (mask != null && mask.pixels[y][x] == 0)
124                                        continue;
125
126                                if (lightnessLower <= L.pixels[y][x] && L.pixels[y][x] <= lightnessUpper && S.pixels[y][x] > satLower) {
127                                        final double hue = H.pixels[y][x];
128                                        final double sat = S.pixels[y][x];
129
130                                        if (skyLower <= hue && hue <= skyUpper) {
131                                                skyMean += sat;
132                                                skyN++;
133                                        }
134
135                                        if (skinLower <= hue && hue <= skinUpper) {
136                                                skinMean += sat;
137                                                skinN++;
138                                        }
139
140                                        if (grassLower <= hue && hue <= grassUpper) {
141                                                grassMean += sat;
142                                                grassN++;
143                                        }
144                                }
145                        }
146                }
147
148                if (skyN != 0)
149                        skyMean /= skyN;
150                if (skinN != 0)
151                        skinMean /= skinN;
152                if (grassN != 0)
153                        grassMean /= grassN;
154        }
155
156        /**
157         * Get the naturalness value for the image previously analysed with
158         * {@link #analyseImage(MBFImage)}.
159         * 
160         * @return the naturalness value
161         */
162        public double getNaturalness() {
163                final double NSkin = Math.exp(-0.5 * Math.pow((skinMean - 0.76) / (0.52), 2));
164                final double NGrass = Math.exp(-0.5 * Math.pow((grassMean - 0.81) / (0.53), 2));
165                final double NSky = Math.exp(-0.5 * Math.pow((skyMean - 0.43) / (0.22), 2));
166
167                final double nPixels = skinN + grassN + skyN;
168
169                if (nPixels == 0)
170                        return 0;
171
172                final double wSkin = skinN / nPixels;
173                final double wGrass = grassN / nPixels;
174                final double wSky = skyN / nPixels;
175
176                return wSkin * NSkin + wGrass * NGrass + wSky * NSky;
177        }
178
179        @Override
180        public DoubleFV getFeatureVector() {
181                return new DoubleFV(new double[] { getNaturalness() });
182        }
183}