View Javadoc

1   /**
2    * Copyright (c) 2011, The University of Southampton and the individual contributors.
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    *
8    *   * 	Redistributions of source code must retain the above copyright notice,
9    * 	this list of conditions and the following disclaimer.
10   *
11   *   *	Redistributions in binary form must reproduce the above copyright notice,
12   * 	this list of conditions and the following disclaimer in the documentation
13   * 	and/or other materials provided with the distribution.
14   *
15   *   *	Neither the name of the University of Southampton nor the names of its
16   * 	contributors may be used to endorse or promote products derived from this
17   * 	software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package org.openimaj.image.feature.global;
31  
32  import gnu.trove.map.hash.TObjectFloatHashMap;
33  import gnu.trove.procedure.TObjectFloatProcedure;
34  
35  import org.openimaj.citation.annotation.Reference;
36  import org.openimaj.citation.annotation.ReferenceType;
37  import org.openimaj.feature.DoubleFV;
38  import org.openimaj.feature.FeatureVectorProvider;
39  import org.openimaj.image.MBFImage;
40  import org.openimaj.image.analyser.ImageAnalyser;
41  import org.openimaj.image.pixel.ConnectedComponent;
42  import org.openimaj.image.pixel.PixelSet;
43  import org.openimaj.image.saliency.AchantaSaliency;
44  import org.openimaj.image.saliency.YehSaliency;
45  import org.openimaj.image.segmentation.FelzenszwalbHuttenlocherSegmenter;
46  import org.openimaj.math.geometry.point.Point2dImpl;
47  
48  /**
49   * Implementation of the rule-of-thirds algorithm described by Yeh et al.
50   * <p>
51   * I've assumed that the distances to the power-points should be normalized with
52   * respect to the image size - this isn't explicit in the paper, but given that
53   * the sigma of the gaussian is fixed, it seems likely...
54   *
55   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
56   */
57  @Reference(
58  		type = ReferenceType.Inproceedings,
59  		author = { "Che-Hua Yeh", "Yuan-Chen Ho", "Brian A. Barsky", "Ming Ouhyoung" },
60  		title = "Personalized Photograph Ranking and Selection System",
61  		year = "2010",
62  		booktitle = "Proceedings of ACM Multimedia",
63  		pages = { "211", "220" },
64  		month = "October",
65  		customData = { "location", "Florence, Italy" })
66  public class RuleOfThirds implements ImageAnalyser<MBFImage>, FeatureVectorProvider<DoubleFV> {
67  	private static final double SIGMA = 0.17;
68  	private static final Point2dImpl[] powerPoints = getPowerPoints();
69  
70  	YehSaliency saliencyGenerator;
71  	private double asSum;
72  	private double aseSum;
73  
74  	/**
75  	 * Construct a new {@link RuleOfThirds} with the default settings for the
76  	 * {@link YehSaliency} algorithm.
77  	 */
78  	public RuleOfThirds() {
79  		saliencyGenerator = new YehSaliency();
80  	}
81  
82  	/**
83  	 * Construct a new {@link RuleOfThirds} with the given values for the
84  	 * {@link YehSaliency} algorithm.
85  	 *
86  	 * @param saliencySigma
87  	 *            smoothing for the {@link AchantaSaliency} class
88  	 * @param segmenterSigma
89  	 *            smoothing for {@link FelzenszwalbHuttenlocherSegmenter}.
90  	 * @param k
91  	 *            k value for {@link FelzenszwalbHuttenlocherSegmenter}.
92  	 * @param minSize
93  	 *            minimum region size for
94  	 *            {@link FelzenszwalbHuttenlocherSegmenter}.
95  	 */
96  	public RuleOfThirds(float saliencySigma, float segmenterSigma, float k, int minSize) {
97  		saliencyGenerator = new YehSaliency(saliencySigma, segmenterSigma, k, minSize);
98  	}
99  
100 	@Override
101 	public DoubleFV getFeatureVector() {
102 		if (asSum == 0)
103 			new DoubleFV(new double[] { 0 });
104 		return new DoubleFV(new double[] { aseSum / asSum });
105 	}
106 
107 	/*
108 	 * (non-Javadoc)
109 	 *
110 	 * @see
111 	 * org.openimaj.image.processor.ImageProcessor#processImage(org.openimaj
112 	 * .image.Image)
113 	 */
114 	@Override
115 	public void analyseImage(MBFImage image) {
116 		final int width = image.getWidth();
117 		final int height = image.getHeight();
118 
119 		image.analyseWith(saliencyGenerator);
120 		final TObjectFloatHashMap<ConnectedComponent> componentMap = saliencyGenerator.getSaliencyComponents();
121 
122 		asSum = 0;
123 		aseSum = 0;
124 		componentMap.forEachEntry(new TObjectFloatProcedure<ConnectedComponent>() {
125 			@Override
126 			public boolean execute(ConnectedComponent c, float s) {
127 				final double as = c.calculateArea() * s;
128 
129 				final double D = closestDistance(c, width, height);
130 
131 				asSum += as;
132 				aseSum += as * Math.exp(-(D * D) / (2 * SIGMA));
133 
134 				return true;
135 			}
136 		});
137 	}
138 
139 	private double closestDistance(PixelSet cc, int width, int height) {
140 		final double centroid[] = cc.calculateCentroid();
141 		double minDistance = Double.MAX_VALUE;
142 
143 		for (final Point2dImpl pt : powerPoints) {
144 			final double dx = (centroid[0] / width) - pt.x;
145 			final double dy = (centroid[1] / width) - pt.y;
146 			final double d = dx * dx + dy * dy;
147 
148 			if (d < minDistance)
149 				minDistance = d;
150 		}
151 
152 		return Math.sqrt(minDistance);
153 	}
154 
155 	private static Point2dImpl[] getPowerPoints() {
156 		return new Point2dImpl[] {
157 				new Point2dImpl(1 / 3f, 1 / 3f),
158 				new Point2dImpl(2 / 3f, 1 / 3f),
159 				new Point2dImpl(1 / 3f, 2 / 3f),
160 				new Point2dImpl(2 / 3f, 2 / 3f) };
161 	}
162 }