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.saliency; 031 032import java.util.Arrays; 033 034import org.openimaj.citation.annotation.Reference; 035import org.openimaj.citation.annotation.ReferenceType; 036import org.openimaj.image.FImage; 037import org.openimaj.image.processing.convolution.AverageBoxFilter; 038import org.openimaj.image.processing.convolution.FConvolution; 039 040/** 041 * Construct a map that shows the "focus" of each pixel. 042 * A value of 0 in the output corresponds to a sharp pixel, whilst higher 043 * values correspond to more blurred pixels. 044 * 045 * Algorithm based on: 046 * Yiwen Luo and Xiaoou Tang. 2008. 047 * Photo and Video Quality Evaluation: Focusing on the Subject. 048 * In Proceedings of the 10th European Conference on Computer Vision: 049 * Part III (ECCV '08), David Forsyth, Philip Torr, and Andrew Zisserman (Eds.). 050 * Springer-Verlag, Berlin, Heidelberg, 386-399. DOI=10.1007/978-3-540-88690-7_29 051 * http://dx.doi.org/10.1007/978-3-540-88690-7_29 052 * 053 * Note that this is not scale invariant - you will get different results with 054 * different sized images... 055 * 056 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 057 */ 058@Reference( 059 type = ReferenceType.Inproceedings, 060 author = { "Luo, Yiwen", "Tang, Xiaoou" }, 061 title = "Photo and Video Quality Evaluation: Focusing on the Subject", 062 year = "2008", 063 booktitle = "Proceedings of the 10th European Conference on Computer Vision: Part III", 064 pages = { "386", "", "399" }, 065 url = "http://dx.doi.org/10.1007/978-3-540-88690-7_29", 066 publisher = "Springer-Verlag", 067 series = "ECCV '08", 068 customData = { 069 "isbn", "978-3-540-88689-1", 070 "location", "Marseille, France", 071 "numpages", "14", 072 "doi", "10.1007/978-3-540-88690-7_29", 073 "acmid", "1478204", 074 "address", "Berlin, Heidelberg" 075 } 076 ) 077public class DepthOfFieldEstimator implements SaliencyMapGenerator<FImage> { 078 private static FConvolution DX_FILTER = new FConvolution(new float[][] {{1, -1}}); 079 private static FConvolution DY_FILTER = new FConvolution(new float[][] {{1}, {-1}}); 080 081 protected int maxKernelSize = 50; 082 protected int kernelSizeStep = 1; 083 protected int nbins = 41; 084 085 protected int windowSize = 3; 086 087 protected float[][] xHistograms; 088 protected float[][] yHistograms; 089 private FImage map; 090 091 /** 092 * Construct with the given parameters. 093 * @param maxKernelSize Maximum kernel size. 094 * @param kernelSizeStep Kernel step size. 095 * @param nbins Number of bins. 096 * @param windowSize window size. 097 */ 098 public DepthOfFieldEstimator(int maxKernelSize, int kernelSizeStep, int nbins, int windowSize) { 099 this.maxKernelSize = maxKernelSize; 100 this.kernelSizeStep = kernelSizeStep; 101 this.nbins = nbins; 102 this.windowSize = windowSize; 103 this.xHistograms = new float[maxKernelSize / kernelSizeStep][nbins]; 104 this.yHistograms = new float[maxKernelSize / kernelSizeStep][nbins]; 105 } 106 107 /** 108 * Construct with the default values (max kernel size = 50, step size = 1, 41 bins, window size of 3). 109 */ 110 public DepthOfFieldEstimator() { 111 this.xHistograms = new float[maxKernelSize / kernelSizeStep][nbins]; 112 this.yHistograms = new float[maxKernelSize / kernelSizeStep][nbins]; 113 } 114 115 protected void clearHistograms() { 116 for (float [] h : xHistograms) 117 Arrays.fill(h, 0); 118 119 for (float [] h : yHistograms) 120 Arrays.fill(h, 0); 121 } 122 123 /* (non-Javadoc) 124 * @see org.openimaj.image.processor.ImageProcessor#processImage(org.openimaj.image.Image) 125 */ 126 @Override 127 public void analyseImage(FImage image) { 128 clearHistograms(); 129 130 for (int i=0; i<maxKernelSize; i+=kernelSizeStep) { 131 FImage blurred = image.process(new AverageBoxFilter(i+1, i+1)); 132 FImage dx = blurred.process(DX_FILTER); 133 FImage dy = blurred.process(DY_FILTER); 134 135 makeLogHistogram(xHistograms[i], dx); 136 makeLogHistogram(yHistograms[i], dy); 137 } 138 139 FImage dx = image.process(DX_FILTER); 140 FImage dy = image.process(DY_FILTER); 141 map = new FImage(image.width, image.height); 142 for (int y=0; y<image.height; y++) { 143 for (int x=0; x<image.width; x++) { 144 if (x == 0 || y == 0 || x == image.width-1 || y == image.height-1) { 145 map.pixels[y][x] = maxKernelSize; 146 } else { 147 int bestModel = 0; 148 double bestLL = calculatedLogLikelihood(x, y, dx, dy, 0); 149 150 for (int i=1; i<maxKernelSize; i+=kernelSizeStep) { 151 double newLL = calculatedLogLikelihood(x, y, dx, dy, i); 152 153 if (newLL > bestLL) { 154 bestLL = newLL; 155 bestModel = i; 156 } 157 } 158 159 map.pixels[y][x] = bestModel; 160 } 161 } 162 } 163 } 164 165 private double calculatedLogLikelihood(int x, int y, FImage dx, FImage dy, int level) { 166 int border = windowSize / 2; 167 168 double LL = 0; 169 for (int j=y-border; j<=y+border; j++) { 170 for (int i=x-border; i<=x+border; i++) { 171 float vx = (dx.pixels[j][i] + 1) / 2; 172 int bx = (int) (vx * nbins); 173 if (bx >= nbins) bx --; 174 175 float vy = (dy.pixels[j][i] + 1) / 2; 176 int by = (int) (vy * nbins); 177 if (by >= nbins) by --; 178 179 LL += xHistograms[level][bx] + yHistograms[level][by]; 180 } 181 } 182 return LL; 183 } 184 185 private void makeLogHistogram(float[] h, FImage im) { 186 int sum = 0; 187 for (int y=0; y<im.height; y++) { 188 for (int x=0; x<im.width; x++) { 189 float v = (im.pixels[y][x] + 1) / 2; //norm to 0..1 190 191 int bin = (int) (v * nbins); 192 if (bin >= nbins) bin --; 193 194 h[bin]++; 195 sum++; 196 } 197 } 198 199 for (int i=0; i<nbins; i++) { 200 if (h[i] == 0) 201 h[i] = 0.00000001f; //a really small value for smoothing 202 203 h[i] = (float) Math.log(h[i] / (double)sum); 204 } 205 } 206 207 @Override 208 public FImage getSaliencyMap() { 209 return map; 210 } 211}