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.saliency;
31  
32  import java.util.Arrays;
33  
34  import org.openimaj.citation.annotation.Reference;
35  import org.openimaj.citation.annotation.ReferenceType;
36  import org.openimaj.image.FImage;
37  import org.openimaj.image.processing.convolution.AverageBoxFilter;
38  import org.openimaj.image.processing.convolution.FConvolution;
39  
40  /**
41   * Construct a map that shows the "focus" of each pixel. A value of 0 in the
42   * output corresponds to a sharp pixel, whilst higher values correspond to more
43   * blurred pixels.
44   *
45   * Algorithm based on: Yiwen Luo and Xiaoou Tang. 2008. Photo and Video Quality
46   * Evaluation: Focusing on the Subject. In Proceedings of the 10th European
47   * Conference on Computer Vision: Part III (ECCV '08), David Forsyth, Philip
48   * Torr, and Andrew Zisserman (Eds.). Springer-Verlag, Berlin, Heidelberg,
49   * 386-399. DOI=10.1007/978-3-540-88690-7_29
50   * http://dx.doi.org/10.1007/978-3-540-88690-7_29
51   *
52   * Note that this is not scale invariant - you will get different results with
53   * different sized images...
54   *
55   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
56   */
57  @Reference(
58  		type = ReferenceType.Inproceedings,
59  		author = { "Luo, Yiwen", "Tang, Xiaoou" },
60  		title = "Photo and Video Quality Evaluation: Focusing on the Subject",
61  		year = "2008",
62  		booktitle = "Proceedings of the 10th European Conference on Computer Vision: Part III",
63  		pages = { "386", "", "399" },
64  		url = "http://dx.doi.org/10.1007/978-3-540-88690-7_29",
65  		publisher = "Springer-Verlag",
66  		series = "ECCV '08",
67  		customData = {
68  				"isbn", "978-3-540-88689-1",
69  				"location", "Marseille, France",
70  				"numpages", "14",
71  				"doi", "10.1007/978-3-540-88690-7_29",
72  				"acmid", "1478204",
73  				"address", "Berlin, Heidelberg"
74  		})
75  public class DepthOfFieldEstimator implements SaliencyMapGenerator<FImage> {
76  	private static FConvolution DX_FILTER = new FConvolution(new float[][] { { 1, -1 } });
77  	private static FConvolution DY_FILTER = new FConvolution(new float[][] { { 1 }, { -1 } });
78  
79  	protected int maxKernelSize = 50;
80  	protected int kernelSizeStep = 1;
81  	protected int nbins = 41;
82  
83  	protected int windowSize = 3;
84  
85  	protected float[][] xHistograms;
86  	protected float[][] yHistograms;
87  	private FImage map;
88  
89  	/**
90  	 * Construct with the given parameters.
91  	 * 
92  	 * @param maxKernelSize
93  	 *            Maximum kernel size.
94  	 * @param kernelSizeStep
95  	 *            Kernel step size.
96  	 * @param nbins
97  	 *            Number of bins.
98  	 * @param windowSize
99  	 *            window size.
100 	 */
101 	public DepthOfFieldEstimator(int maxKernelSize, int kernelSizeStep, int nbins, int windowSize) {
102 		this.maxKernelSize = maxKernelSize;
103 		this.kernelSizeStep = kernelSizeStep;
104 		this.nbins = nbins;
105 		this.windowSize = windowSize;
106 		this.xHistograms = new float[maxKernelSize / kernelSizeStep][nbins];
107 		this.yHistograms = new float[maxKernelSize / kernelSizeStep][nbins];
108 	}
109 
110 	/**
111 	 * Construct with the default values (max kernel size = 50, step size = 1,
112 	 * 41 bins, window size of 3).
113 	 */
114 	public DepthOfFieldEstimator() {
115 		this.xHistograms = new float[maxKernelSize / kernelSizeStep][nbins];
116 		this.yHistograms = new float[maxKernelSize / kernelSizeStep][nbins];
117 	}
118 
119 	protected void clearHistograms() {
120 		for (final float[] h : xHistograms)
121 			Arrays.fill(h, 0);
122 
123 		for (final float[] h : yHistograms)
124 			Arrays.fill(h, 0);
125 	}
126 
127 	/*
128 	 * (non-Javadoc)
129 	 * 
130 	 * @see
131 	 * org.openimaj.image.processor.ImageProcessor#processImage(org.openimaj.
132 	 * image.Image)
133 	 */
134 	@Override
135 	public void analyseImage(FImage image) {
136 		clearHistograms();
137 
138 		for (int i = 0, j = 0; i < maxKernelSize; i += kernelSizeStep, j++) {
139 			final FImage blurred = image.process(new AverageBoxFilter(i + 1, i + 1));
140 			final FImage dx = blurred.process(DX_FILTER);
141 			final FImage dy = blurred.process(DY_FILTER);
142 
143 			makeLogHistogram(xHistograms[j], dx);
144 			makeLogHistogram(yHistograms[j], dy);
145 		}
146 
147 		final FImage dx = image.process(DX_FILTER);
148 		final FImage dy = image.process(DY_FILTER);
149 		map = new FImage(image.width, image.height);
150 		for (int y = 0; y < image.height; y++) {
151 			for (int x = 0; x < image.width; x++) {
152 				if (x == 0 || y == 0 || x == image.width - 1 || y == image.height - 1) {
153 					map.pixels[y][x] = maxKernelSize;
154 				} else {
155 					int bestModel = 0;
156 					double bestLL = calculatedLogLikelihood(x, y, dx, dy, 0);
157 
158 					for (int i = 1, j = 1; i < maxKernelSize; i += kernelSizeStep, j++) {
159 						final double newLL = calculatedLogLikelihood(x, y, dx, dy, j);
160 
161 						if (newLL > bestLL) {
162 							bestLL = newLL;
163 							bestModel = i;
164 						}
165 					}
166 
167 					map.pixels[y][x] = bestModel;
168 				}
169 			}
170 		}
171 	}
172 
173 	private double calculatedLogLikelihood(int x, int y, FImage dx, FImage dy, int level) {
174 		final int border = windowSize / 2;
175 
176 		double LL = 0;
177 		for (int j = y - border; j <= y + border; j++) {
178 			for (int i = x - border; i <= x + border; i++) {
179 				final float vx = (dx.pixels[j][i] + 1) / 2;
180 				int bx = (int) (vx * nbins);
181 				if (bx >= nbins)
182 					bx--;
183 
184 				final float vy = (dy.pixels[j][i] + 1) / 2;
185 				int by = (int) (vy * nbins);
186 				if (by >= nbins)
187 					by--;
188 
189 				LL += xHistograms[level][bx] + yHistograms[level][by];
190 			}
191 		}
192 		return LL;
193 	}
194 
195 	private void makeLogHistogram(float[] h, FImage im) {
196 		int sum = 0;
197 		for (int y = 0; y < im.height; y++) {
198 			for (int x = 0; x < im.width; x++) {
199 				final float v = (im.pixels[y][x] + 1) / 2; // norm to 0..1
200 
201 				int bin = (int) (v * nbins);
202 				if (bin >= nbins)
203 					bin--;
204 
205 				h[bin]++;
206 				sum++;
207 			}
208 		}
209 
210 		for (int i = 0; i < nbins; i++) {
211 			if (h[i] == 0)
212 				h[i] = 0.00000001f; // a really small value for smoothing
213 
214 			h[i] = (float) Math.log(h[i] / (double) sum);
215 		}
216 	}
217 
218 	@Override
219 	public FImage getSaliencyMap() {
220 		return map;
221 	}
222 }