1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package org.openimaj.image.processing.restoration.inpainting;
31
32 import java.util.PriorityQueue;
33
34 import org.openimaj.citation.annotation.Reference;
35 import org.openimaj.citation.annotation.ReferenceType;
36 import org.openimaj.citation.annotation.References;
37 import org.openimaj.image.FImage;
38 import org.openimaj.image.Image;
39 import org.openimaj.image.pixel.FValuePixel;
40 import org.openimaj.image.processing.morphology.Dilate;
41 import org.openimaj.image.processing.morphology.StructuringElement;
42 import org.openimaj.image.processor.SinglebandImageProcessor;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 @References(references = {
81 @Reference(
82 type = ReferenceType.Article,
83 author = { "Telea, Alexandru" },
84 title = "An Image Inpainting Technique Based on the Fast Marching Method.",
85 year = "2004",
86 journal = "J. Graphics, GPU, & Game Tools",
87 pages = { "23", "34" },
88 url = "http://dblp.uni-trier.de/db/journals/jgtools/jgtools9.html#Telea04",
89 number = "1",
90 volume = "9",
91 customData = {
92 "biburl", "http://www.bibsonomy.org/bibtex/2b0bf54e265d011a8e1fe256e6fcf556b/dblp",
93 "ee", "http://dx.doi.org/10.1080/10867651.2004.10487596",
94 "keywords", "dblp"
95 }
96 ),
97 @Reference(
98 type = ReferenceType.Inproceedings,
99 author = { "J. A. Sethian" },
100 title = "A Fast Marching Level Set Method for Monotonically Advancing Fronts",
101 year = "1995",
102 booktitle = "Proc. Nat. Acad. Sci",
103 pages = { "1591", "", "1595" }
104 )
105 })
106 @SuppressWarnings("javadoc")
107 public abstract class AbstractFMMInpainter<IMAGE extends Image<?, IMAGE> & SinglebandImageProcessor.Processable<Float, FImage, IMAGE>>
108 extends
109 AbstractImageMaskInpainter<IMAGE>
110 {
111 private static final int[][] DELTAS = new int[][] { { 0, -1 }, { -1, 0 }, { 0, 1 }, { 1, 0 } };
112
113
114
115
116 protected static byte KNOWN = 0;
117
118
119
120
121 protected static byte BAND = 1;
122
123
124
125
126 protected static byte UNKNOWN = 2;
127
128
129
130
131 protected byte[][] flag;
132
133
134
135
136 protected FImage timeMap;
137
138
139
140
141 protected PriorityQueue<FValuePixel> heap;
142
143 @Override
144 protected void initMask() {
145 final FImage outside = mask.process(new Dilate(StructuringElement.CROSS), true);
146
147 flag = new byte[mask.height][mask.width];
148 timeMap = new FImage(outside.width, outside.height);
149
150 heap = new PriorityQueue<FValuePixel>(10, FValuePixel.ValueComparator.INSTANCE);
151
152 for (int y = 0; y < mask.height; y++) {
153 for (int x = 0; x < mask.width; x++) {
154 final int band = (int) (outside.pixels[y][x] - mask.pixels[y][x]);
155 flag[y][x] = (byte) ((2 * outside.pixels[y][x]) - band);
156
157 if (flag[y][x] == UNKNOWN)
158 timeMap.pixels[y][x] = Float.MAX_VALUE;
159
160 if (band != 0) {
161 heap.add(new FValuePixel(x, y, timeMap.pixels[y][x]));
162 }
163 }
164 }
165 }
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180 protected float solveEikonalStep(int x1, int y1, int x2, int y2)
181 {
182 float soln = Float.MAX_VALUE;
183
184 final float t1 = timeMap.pixels[y1][x1];
185 final float t2 = timeMap.pixels[y2][x2];
186
187 if (flag[y1][x1] == KNOWN) {
188 if (flag[y2][x2] == KNOWN) {
189 final float r = (float) Math.sqrt(2 - (t1 - t2) * (t1 - t2));
190 float s = (t1 + t2 - r) * 0.5f;
191
192 if (s >= t1 && s >= t2) {
193 soln = s;
194 } else {
195 s += r;
196
197 if (s >= t1 && s >= t2) {
198 soln = s;
199 }
200 }
201 } else {
202 soln = 1 + t1;
203 }
204 } else if (flag[y2][x2] == KNOWN) {
205 soln = 1 + t2;
206 }
207
208 return soln;
209 }
210
211 @Override
212 public void performInpainting(IMAGE image) {
213 final int width = image.getWidth();
214 final int height = image.getHeight();
215
216 while (!heap.isEmpty()) {
217 final FValuePixel pix = heap.poll();
218 final int x = pix.x;
219 final int y = pix.y;
220 flag[y][x] = KNOWN;
221
222 if ((x <= 1) || (y <= 1) || (x >= width - 2) || (y >= height - 2))
223 continue;
224
225 for (final int[] p : DELTAS) {
226 final int xp = p[0] + x, yp = p[1] + y;
227
228 if (flag[yp][xp] != KNOWN) {
229 timeMap.pixels[yp][xp] = Math.min(Math.min(Math.min(
230 solveEikonalStep(xp - 1, yp, xp, yp - 1),
231 solveEikonalStep(xp + 1, yp, xp, yp - 1)),
232 solveEikonalStep(xp - 1, yp, xp, yp + 1)),
233 solveEikonalStep(xp + 1, yp, xp, yp + 1));
234
235 if (flag[yp][xp] == UNKNOWN) {
236 flag[yp][xp] = BAND;
237
238 heap.offer(new FValuePixel(xp, yp, timeMap.pixels[yp][xp]));
239
240 inpaint(xp, yp, image);
241 }
242 }
243 }
244 }
245 }
246
247
248
249
250
251
252
253
254
255
256
257 protected abstract void inpaint(int x, int y, IMAGE image);
258 }