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.analysis.algorithm;
31  
32  import java.io.File;
33  import java.io.IOException;
34  
35  import org.openimaj.image.DisplayUtilities;
36  import org.openimaj.image.FImage;
37  import org.openimaj.image.ImageUtilities;
38  import org.openimaj.image.analyser.ImageAnalyser;
39  
40  /**
41   * See http://people.cs.uchicago.edu/~pff/papers/dt.pdf
42   * 
43   * An efficient euclidean distance transform applicable to all greyscale images. The distance of each pixel to the closest 
44   * valid pixel is given. In this case a pixel is considered valid when it is less than Float.MAX_VALUE.
45   * 
46   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
47   */
48  public class EuclideanDistanceTransform implements ImageAnalyser<FImage> {
49  	FImage distances;
50  	int [][] indices;
51  
52  	/* (non-Javadoc)
53  	 * @see org.openimaj.image.analyser.ImageAnalyser#analyseImage(org.openimaj.image.Image)
54  	 */
55  	@Override
56  	public void analyseImage(FImage image) {
57  		if (distances == null || distances.height != image.height || distances.width != distances.height) {
58  			distances = new FImage(image.width, image.height);
59  			indices = new int[image.height][image.width];
60  		}
61  
62  		squaredEuclideanDistance(image, distances, indices);
63  	}
64  
65  	/**
66  	 * @return the distance transformed image (unnormalised)
67  	 */
68  	public FImage getDistances() {
69  		return distances;
70  	}
71  
72  	/**
73  	 * @return the indecies of the closest pixel to any given pixel
74  	 */
75  	public int[][] getIndices() {
76  		return indices;
77  	}
78  
79  	protected static void DT1D(float [] f, float [] d, int [] v, int [] l, float [] z) {
80  		int k = 0;
81  
82  		v[0] = 0;
83  		z[0] = -Float.MAX_VALUE;
84  		z[1] = Float.MAX_VALUE;
85  
86  		for (int q = 1; q < f.length; q++)
87  		{
88  			float s  = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]);
89  
90  			while (s <= z[k]) {
91  				k--;
92  				s  = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]);
93  			}
94  			k++;
95  			v[k] = q;
96  			z[k] = s;
97  			z[k + 1] = Float.MAX_VALUE;
98  		}
99  
100 		k = 0;
101 		for (int q = 0; q < f.length; q++)
102 		{
103 			while (z[k + 1] < q)
104 				k++;
105 
106 			d[q] = (q - v[k]) * (q - v[k]) + f[v[k]];
107 			l[q] = v[k];
108 		}
109 	}
110 
111 	/**
112 	 * Calculate the squared euclidean distance transform of a binary image with
113 	 * foreground pixels set to 1 and background set to 0.
114 	 * @param image the image to be transformed.
115 	 * @param distances the distance of each pixel to the closest 1-pixel
116 	 * @param indices the index of the closes valid pixel
117 	 */
118 	public static void squaredEuclideanDistanceBinary(FImage image, FImage distances, int[][] indices) {
119 		float [] f = new float[Math.max(image.height, image.width)];
120 		float [] d = new float[f.length];
121 		int [] v = new int[f.length];
122 		int [] l = new int[f.length];
123 		float [] z = new float[f.length + 1];
124 
125 		for (int x=0; x<image.width; x++) {
126 			for (int y=0; y<image.height; y++) {
127 				f[y] = image.pixels[y][x] == 0 ? Float.MAX_VALUE : 0;
128 			}
129 
130 			DT1D(f, d, v, l, z);
131 			for (int y = 0; y < image.height; y++) {
132 				distances.pixels[y][x] = d[y];
133 				indices[y][x] = (l[y] * image.width) + x; //this is now row-major
134 			}
135 		}
136 		
137 		for (int y = 0; y < image.height; y++) {
138 			DT1D(distances.pixels[y], d, v, l, z);
139 
140 			for (int x = 0; x < image.width; x++)
141 				l[x] = indices[y][l[x]];
142 
143 			for (int x = 0; x < image.width; x++)
144 			{
145 				distances.pixels[y][x] = d[x];
146 				indices[y][x] = l[x];
147 			}
148 		}
149 	}
150 	
151 	/**
152 	 * The static function which underlies EuclideanDistanceTransform. Provide an image, fill distances and indices with 
153 	 * the distance image and the closest pixel indices. Typically, for the binary case, valid pixels are set to 0 and invalid
154 	 * pixels are set to Float.MAX_VALUE or Float.POSITIVE_INFINITY.
155 	 * 
156 	 * @param image the image to be transformed. Each pixel is considered valid except those of value Float.MAX_VALUE
157 	 * @param distances the distance of each pixel to the closest non-Float.MAX_VALUE pixel
158 	 * @param indices the index of the closes valid pixel
159 	 */
160 	public static void squaredEuclideanDistance(FImage image, FImage distances, int[][] indices) {
161 		float [] f = new float[Math.max(image.height, image.width)];
162 		float [] d = new float[f.length];
163 		int [] v = new int[f.length];
164 		int [] l = new int[f.length];
165 		float [] z = new float[f.length + 1];
166 
167 		for (int x=0; x<image.width; x++) {
168 			for (int y=0; y<image.height; y++) {
169 				f[y] = Float.isInfinite(image.pixels[y][x]) ? (image.pixels[y][x] > 0 ? Float.MAX_VALUE : -Float.MAX_VALUE) : image.pixels[y][x];
170 			}
171 
172 			DT1D(f, d, v, l, z);
173 			for (int y = 0; y < image.height; y++) {
174 				distances.pixels[y][x] = d[y];
175 				indices[y][x] = (l[y] * image.width) + x; //this is now row-major
176 			}
177 		}
178 		
179 		for (int y = 0; y < image.height; y++) {
180 			DT1D(distances.pixels[y], d, v, l, z);
181 
182 			for (int x = 0; x < image.width; x++)
183 				l[x] = indices[y][l[x]];
184 
185 			for (int x = 0; x < image.width; x++)
186 			{
187 				distances.pixels[y][x] = d[x];
188 				indices[y][x] = l[x];
189 			}
190 		}
191 	}
192 	
193 	/**
194 	 * Test the distance transform
195 	 * @param args
196 	 * @throws IOException
197 	 */
198 	public static void main(String args[]) throws IOException{
199 		FImage i = ImageUtilities.readF(new File("/Users/ss/Desktop/tache.jpg"));
200 		EuclideanDistanceTransform etrans = new EuclideanDistanceTransform();
201 //		i.processInplace(new CannyEdgeDetector());
202 		i.inverse();
203 		for(int x = 0;x < i.width; x++)
204 			for(int y = 0; y < i.height; y++) 
205 				if(i.pixels[y][x] == 1.0f) 
206 					i.setPixel(x, y, Float.MAX_VALUE);
207 		DisplayUtilities.display(i);
208 		i.analyseWith(etrans);
209 		i = etrans.getDistances();
210 		i.normalise();
211 		DisplayUtilities.display(i);
212 	}
213 }