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 gnu.trove.map.hash.TObjectFloatHashMap; 033import gnu.trove.procedure.TObjectFloatProcedure; 034 035import org.openimaj.citation.annotation.Reference; 036import org.openimaj.citation.annotation.ReferenceType; 037import org.openimaj.citation.annotation.References; 038import org.openimaj.feature.DoubleFV; 039import org.openimaj.feature.FeatureVectorProvider; 040import org.openimaj.image.FImage; 041import org.openimaj.image.MBFImage; 042import org.openimaj.image.analyser.ImageAnalyser; 043import org.openimaj.image.pixel.ConnectedComponent; 044import org.openimaj.image.pixel.statistics.MaskingHistogramModel; 045import org.openimaj.image.processor.connectedcomponent.render.BoundingBoxRenderer; 046import org.openimaj.image.saliency.AchantaSaliency; 047import org.openimaj.image.saliency.YehSaliency; 048import org.openimaj.image.segmentation.FelzenszwalbHuttenlocherSegmenter; 049import org.openimaj.math.statistics.distribution.MultidimensionalHistogram; 050import org.openimaj.util.array.ArrayUtils; 051 052/** 053 * Estimate the simplicity of an image by looking at the colour distribution of 054 * the background. 055 * <p> 056 * Algorithm based on that proposed by Yiwen Luo and Xiaoou Tang, but modified 057 * to use the foreground detection approach suggested in Che-Hua Yeh et al. 058 * 059 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 060 */ 061@References(references = { 062 @Reference( 063 type = ReferenceType.Inproceedings, 064 author = { "Luo, Yiwen", "Tang, Xiaoou" }, 065 title = "Photo and Video Quality Evaluation: Focusing on the Subject", 066 year = "2008", 067 booktitle = "Proceedings of the 10th European Conference on Computer Vision: Part III", 068 pages = { "386", "399" }, 069 url = "http://dx.doi.org/10.1007/978-3-540-88690-7_29", 070 publisher = "Springer-Verlag", 071 series = "ECCV '08", 072 customData = { "isbn", "978-3-540-88689-1", "location", "Marseille, France", "numpages", "14", "doi", 073 "10.1007/978-3-540-88690-7_29", "acmid", "1478204", "address", "Berlin, Heidelberg" }), 074 @Reference( 075 type = ReferenceType.Inproceedings, 076 author = { "Che-Hua Yeh", "Yuan-Chen Ho", "Brian A. Barsky", "Ming Ouhyoung" }, 077 title = "Personalized Photograph Ranking and Selection System", 078 year = "2010", 079 booktitle = "Proceedings of ACM Multimedia", 080 pages = { "211", "220" }, 081 month = "October", 082 customData = { "location", "Florence, Italy" }) }) 083public class ModifiedLuoSimplicity implements ImageAnalyser<MBFImage>, FeatureVectorProvider<DoubleFV> { 084 protected YehSaliency extractor; 085 protected float alpha = 0.67f; 086 087 protected int binsPerBand = 16; 088 protected float gamma = 0.01f; 089 protected boolean boxMode = true; 090 protected double simplicity; 091 092 /** 093 * Construct with the default values 094 */ 095 public ModifiedLuoSimplicity() { 096 extractor = new YehSaliency(); 097 } 098 099 /** 100 * Construct with the given values 101 * 102 * @param binsPerBand 103 * the number of histogram bins per colour band 104 * @param gamma 105 * the gamma value for determining the threshold 106 * @param boxMode 107 * whether to extract rectangular boxes for the foreground 108 * regions (true) or to just use the pixels (false) 109 * @param alpha 110 * the alpha value for determining the foreground/background 111 * threshold 112 * @param saliencySigma 113 * smoothing for the {@link AchantaSaliency} class 114 * @param segmenterSigma 115 * smoothing for {@link FelzenszwalbHuttenlocherSegmenter}. 116 * @param k 117 * k value for {@link FelzenszwalbHuttenlocherSegmenter}. 118 * @param minSize 119 * minimum region size for 120 * {@link FelzenszwalbHuttenlocherSegmenter}. 121 */ 122 public ModifiedLuoSimplicity(int binsPerBand, float gamma, boolean boxMode, float alpha, float saliencySigma, 123 float segmenterSigma, float k, int minSize) 124 { 125 extractor = new YehSaliency(saliencySigma, segmenterSigma, k, minSize); 126 this.binsPerBand = binsPerBand; 127 this.gamma = gamma; 128 this.boxMode = boxMode; 129 this.alpha = alpha; 130 } 131 132 /* 133 * (non-Javadoc) 134 * 135 * @see 136 * org.openimaj.image.analyser.ImageAnalyser#analyseImage(org.openimaj.image 137 * .Image) 138 */ 139 @Override 140 public void analyseImage(MBFImage image) { 141 image.analyseWith(extractor); 142 143 FImage mask; 144 if (boxMode) { 145 final TObjectFloatHashMap<ConnectedComponent> componentMap = extractor.getSaliencyComponents(); 146 147 final float max = ArrayUtils.maxValue(componentMap.values()); 148 149 mask = new FImage(image.getWidth(), image.getHeight()); 150 final float thresh = max * alpha; 151 final BoundingBoxRenderer<Float> renderer = new BoundingBoxRenderer<Float>(mask, 1F, true); 152 153 componentMap.forEachEntry(new TObjectFloatProcedure<ConnectedComponent>() { 154 @Override 155 public boolean execute(ConnectedComponent cc, float sal) { 156 if (sal >= thresh) { // note that this is reversed from the 157 // paper, which doesn't seem to make 158 // sense. 159 renderer.process(cc); 160 } 161 162 return true; 163 } 164 }); 165 } else { 166 mask = extractor.getSaliencyMap(); 167 final float maskthresh = mask.max() * alpha; 168 mask = mask.threshold(maskthresh); 169 } 170 171 mask = mask.inverse(); 172 173 final MaskingHistogramModel hm = new MaskingHistogramModel(mask, binsPerBand, binsPerBand, binsPerBand); 174 hm.estimateModel(image); 175 176 final MultidimensionalHistogram fv = hm.getFeatureVector(); 177 final double thresh = gamma * fv.max(); 178 int count = 0; 179 for (final double f : fv.values) { 180 if (f >= thresh) 181 count++; 182 } 183 184 simplicity = (double) count / (double) fv.values.length; 185 } 186 187 @Override 188 public DoubleFV getFeatureVector() { 189 return new DoubleFV(new double[] { simplicity }); 190 } 191}