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.algorithm;
31
32 import org.openimaj.citation.annotation.Reference;
33 import org.openimaj.citation.annotation.ReferenceType;
34 import org.openimaj.image.FImage;
35 import org.openimaj.image.pixel.ConnectedComponent;
36 import org.openimaj.image.pixel.ConnectedComponent.ConnectMode;
37 import org.openimaj.image.processor.SinglebandImageProcessor;
38
39 import net.jafama.FastMath;
40
41
42
43
44
45
46
47 @Reference(
48 type = ReferenceType.Article,
49 author = { "Perona, P.", "Malik, J." },
50 title = "Scale-Space and Edge Detection Using Anisotropic Diffusion",
51 year = "1990",
52 journal = "IEEE Trans. Pattern Anal. Mach. Intell.",
53 pages = { "629", "", "639" },
54 url = "http://dx.doi.org/10.1109/34.56205",
55 month = "July",
56 number = "7",
57 publisher = "IEEE Computer Society",
58 volume = "12",
59 customData = {
60 "issue_date", "July 1990",
61 "issn", "0162-8828",
62 "numpages", "11",
63 "doi", "10.1109/34.56205",
64 "acmid", "78304",
65 "address", "Washington, DC, USA"
66 })
67 public class AnisotropicDiffusion implements SinglebandImageProcessor<Float, FImage> {
68
69
70
71
72
73
74
75 public static interface ConductionCoefficientFunction {
76
77
78
79
80
81
82
83
84
85
86
87 float apply(float grad, int x, int y);
88 }
89
90
91
92
93
94
95
96
97
98
99
100 public static class HighConstrastEdgeFunction implements ConductionCoefficientFunction {
101 private float kappa;
102
103
104
105
106
107
108
109 public HighConstrastEdgeFunction(float kappa) {
110 this.kappa = kappa;
111 }
112
113 @Override
114 public float apply(float val, int x, int y) {
115 final float t = val / kappa;
116 return (float) FastMath.exp(-t * t);
117 }
118 };
119
120
121
122
123
124
125
126
127
128
129
130 public static class WideRegionFunction implements ConductionCoefficientFunction {
131 private float kappa;
132
133
134
135
136
137
138
139 public WideRegionFunction(float kappa) {
140 this.kappa = kappa;
141 }
142
143 @Override
144 public float apply(float val, int x, int y) {
145 final float t = val / kappa;
146 return 1 / (1 + t * t);
147 }
148 };
149
150 protected int numIterations;
151 protected float lambda = 1f / 7f;
152 protected ConductionCoefficientFunction function;
153 protected ConnectedComponent.ConnectMode neighbourMode;
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 public AnisotropicDiffusion(int numIterations, float lambda, ConductionCoefficientFunction function,
169 ConnectMode neighbourMode)
170 {
171 this.numIterations = numIterations;
172 this.lambda = lambda;
173 this.function = function;
174 this.neighbourMode = neighbourMode;
175 }
176
177
178
179
180
181
182
183
184
185
186
187
188
189 public AnisotropicDiffusion(int numIterations, float lambda, ConductionCoefficientFunction function) {
190 this.numIterations = numIterations;
191 this.lambda = lambda;
192 this.function = function;
193 this.neighbourMode = ConnectMode.CONNECT_4;
194 }
195
196 @Override
197 public void processImage(FImage image) {
198 for (int i = 0; i < numIterations; i++) {
199 processImageOneIteration(image);
200 }
201 }
202
203
204
205
206
207
208
209 public void processImageOneIteration(FImage image) {
210 switch (neighbourMode) {
211 case CONNECT_4:
212 processImageOneIteration4(image);
213 case CONNECT_8:
214 processImageOneIteration8(image);
215 }
216 }
217
218 private void processImageOneIteration4(FImage image) {
219 final float[][] tmp = image.clone().pixels;
220
221 for (int y = 0; y < image.height; y++) {
222 final int ym = Math.max(y - 1, 0);
223 final int yp = Math.min(y + 1, image.height - 1);
224
225 for (int x = 0; x < image.width; x++) {
226 final int xm = Math.max(x - 1, 0);
227 final int xp = Math.min(x + 1, image.width - 1);
228
229 final float dN = tmp[ym][x] - tmp[y][x];
230 final float dS = tmp[yp][x] - tmp[y][x];
231 final float dE = tmp[y][xm] - tmp[y][x];
232 final float dW = tmp[y][xp] - tmp[y][x];
233
234 final float cN = function.apply(Math.abs(dN), x, y);
235 final float cS = function.apply(Math.abs(dS), x, y);
236 final float cE = function.apply(Math.abs(dE), x, y);
237 final float cW = function.apply(Math.abs(dW), x, y);
238
239 image.pixels[y][x] += lambda * (cN * dN + cS * dS + cE * dE + cW * dW);
240 }
241 }
242 }
243
244 private void processImageOneIteration8(FImage image) {
245 final float[][] tmp = image.clone().pixels;
246 final float wt = 0.5f;
247
248 for (int y = 0; y < image.height; y++) {
249 final int ym = Math.max(y - 1, 0);
250 final int yp = Math.min(y + 1, image.height - 1);
251
252 for (int x = 0; x < image.width; x++) {
253 final int xm = Math.max(x - 1, 0);
254 final int xp = Math.min(x + 1, image.width - 1);
255
256 final float dN = tmp[ym][x] - tmp[y][x];
257 final float dS = tmp[yp][x] - tmp[y][x];
258 final float dE = tmp[y][xm] - tmp[y][x];
259 final float dW = tmp[y][xp] - tmp[y][x];
260 final float dNE = tmp[ym][xm] - tmp[y][x];
261 final float dSE = tmp[yp][xm] - tmp[y][x];
262 final float dSW = tmp[ym][xp] - tmp[y][x];
263 final float dNW = tmp[ym][xp] - tmp[y][x];
264
265 final float cN = function.apply(Math.abs(dN), x, y);
266 final float cS = function.apply(Math.abs(dS), x, y);
267 final float cE = function.apply(Math.abs(dE), x, y);
268 final float cW = function.apply(Math.abs(dW), x, y);
269 final float cNE = function.apply(Math.abs(dNE), x, y);
270 final float cSE = function.apply(Math.abs(dSE), x, y);
271 final float cSW = function.apply(Math.abs(dSW), x, y);
272 final float cNW = function.apply(Math.abs(dNW), x, y);
273
274 image.pixels[y][x] += lambda
275 * (cN * dN + cS * dS + cE * dE + cW * dW + wt * (cNE * dNE + cSE * dSE + cSW * dSW + cNW * dNW));
276 }
277 }
278 }
279
280
281
282
283
284
285
286
287
288
289 }