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.processing.restoration.inpainting;
31  
32  import java.util.Set;
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.Image;
38  import org.openimaj.image.MBFImage;
39  import org.openimaj.image.pixel.Pixel;
40  import org.openimaj.image.processing.morphology.StructuringElement;
41  import org.openimaj.image.processor.SinglebandImageProcessor;
42  
43  /**
44   * Implementation of Alexandru Telea's FMM-based inpainting algorithm. The
45   * {@link AbstractFMMInpainter} is extended with a method to inpaint pixels
46   * based on the neighbours and explicitly taking into account the image
47   * gradients in the neighbourhood in order to preserve sharp details and smooth
48   * zones.
49   * 
50   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
51   * 
52   * @param <IMAGE>
53   *            The type of image that this processor can process
54   */
55  @Reference(
56  		type = ReferenceType.Article,
57  		author = { "Telea, Alexandru" },
58  		title = "An Image Inpainting Technique Based on the Fast Marching Method.",
59  		year = "2004",
60  		journal = "J. Graphics, GPU, & Game Tools",
61  		pages = { "23", "34" },
62  		url = "http://dblp.uni-trier.de/db/journals/jgtools/jgtools9.html#Telea04",
63  		number = "1",
64  		volume = "9",
65  		customData = {
66  				"biburl", "http://www.bibsonomy.org/bibtex/2b0bf54e265d011a8e1fe256e6fcf556b/dblp",
67  				"doi", "http://dx.doi.org/10.1080/10867651.2004.10487596",
68  				"keywords", "dblp"
69  		})
70  public class TeleaInpainting<IMAGE extends Image<?, IMAGE> & SinglebandImageProcessor.Processable<Float, FImage, IMAGE>>
71  		extends
72  		AbstractFMMInpainter<IMAGE>
73  {
74  	protected Set<Pixel> region;
75  
76  	/**
77  	 * Construct the inpainting operator with the given radius.
78  	 * 
79  	 * @param radius
80  	 *            the radius for selecting how many pixels are used to make
81  	 *            estimates.
82  	 */
83  	public TeleaInpainting(int radius) {
84  		region = StructuringElement.disk(radius).positive;
85  	}
86  
87  	@Override
88  	protected void inpaint(int x, int y, IMAGE image) {
89  		if (image instanceof FImage)
90  			inpaint(x, y, (FImage) image);
91  		else if (image instanceof MBFImage)
92  			inpaint(x, y, (MBFImage) image);
93  		else
94  			throw new UnsupportedOperationException("Image type not supported!");
95  	}
96  
97  	protected void inpaint(int x, int y, FImage input) {
98  		final int width = input.getWidth();
99  		final int height = input.getHeight();
100 		final float gradx_u = gradX(timeMap.pixels, x, y);
101 		final float grady_u = gradY(timeMap.pixels, x, y);
102 
103 		float accum = 0;
104 		float norm = 0;
105 
106 		for (final Pixel p : region) {
107 			final int xx = p.x + x;
108 			final int yy = p.y + y;
109 
110 			if (xx <= 1 || xx >= width - 1 || yy <= 1 || yy >= height - 1)
111 				continue;
112 			if (flag[yy][xx] != KNOWN)
113 				continue;
114 
115 			final int rx = x - xx;
116 			final int ry = y - yy;
117 
118 			// geometric distance.
119 			final float geometricDistance = (float) (1. / ((rx * rx + ry * ry) * Math.sqrt((rx * rx + ry * ry))));
120 
121 			// levelset distance.
122 			final float levelsetDistance = (float) (1. / (1 + Math.abs(timeMap.pixels[yy][xx] - timeMap.pixels[y][x])));
123 
124 			// Dot product of final displacement and gradient vectors.
125 			float direction = Math.abs(rx * gradx_u + ry * grady_u);
126 			if (direction < 0.000001f)
127 				direction = 0.000001f;
128 
129 			final float weight = geometricDistance * levelsetDistance * direction;
130 
131 			accum += weight * input.pixels[yy][xx];
132 			norm += weight;
133 		}
134 
135 		input.pixels[y][x] = accum / norm;
136 	}
137 
138 	protected void inpaint(int x, int y, MBFImage input) {
139 		final int width = input.getWidth();
140 		final int height = input.getHeight();
141 		final float gradx_u = gradX(timeMap.pixels, x, y);
142 		final float grady_u = gradY(timeMap.pixels, x, y);
143 
144 		final int nbands = input.numBands();
145 		final float accum[] = new float[nbands];
146 		float norm = 0;
147 
148 		for (final Pixel p : region) {
149 			final int xx = p.x + x;
150 			final int yy = p.y + y;
151 
152 			if (xx <= 1 || xx >= width - 1 || yy <= 1 || yy >= height - 1)
153 				continue;
154 			if (flag[yy][xx] != KNOWN)
155 				continue;
156 
157 			final int rx = x - xx;
158 			final int ry = y - yy;
159 
160 			// geometric distance.
161 			final float geometricDistance = (float) (1. / ((rx * rx + ry * ry) * Math.sqrt((rx * rx + ry * ry))));
162 
163 			// levelset distance.
164 			final float levelsetDistance = (float) (1. / (1 + Math.abs(timeMap.pixels[yy][xx] - timeMap.pixels[y][x])));
165 
166 			// Dot product of final displacement and gradient vectors.
167 			float direction = Math.abs(rx * gradx_u + ry * grady_u);
168 			if (direction < 0.000001f)
169 				direction = 0.000001f;
170 
171 			final float weight = geometricDistance * levelsetDistance * direction;
172 
173 			for (int i = 0; i < nbands; i++)
174 				accum[i] += weight * input.getBand(i).pixels[yy][xx];
175 			norm += weight;
176 		}
177 
178 		for (int i = 0; i < nbands; i++)
179 			input.getBand(i).pixels[y][x] = accum[i] / norm;
180 	}
181 
182 	private float gradX(float[][] img, int x, int y) {
183 		float grad;
184 
185 		if (flag[y][x + 1] != UNKNOWN) {
186 			if (flag[y][x - 1] != UNKNOWN)
187 				grad = (img[y][x + 1] - img[y][x - 1]) * 0.5f;
188 			else
189 				grad = (img[y][x + 1] - img[y][x]);
190 		} else {
191 			if (flag[y][x - 1] != UNKNOWN)
192 				grad = (img[y][x] - img[y][x - 1]);
193 			else
194 				grad = 0;
195 		}
196 
197 		return grad;
198 	}
199 
200 	private float gradY(float[][] img, int x, int y) {
201 		float grad;
202 
203 		if (flag[y + 1][x] != UNKNOWN) {
204 			if (flag[y - 1][x] != UNKNOWN)
205 				grad = (img[y + 1][x] - img[y - 1][x]) * 0.5f;
206 			else
207 				grad = (img[y + 1][x] - img[y][x]);
208 		} else {
209 			if (flag[y - 1][x] != UNKNOWN)
210 				grad = (img[y][x] - img[y - 1][x]);
211 			else
212 				grad = 0;
213 		}
214 
215 		return grad;
216 	}
217 }