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.transform;
31
32 import java.util.ArrayList;
33 import java.util.List;
34
35 import org.openimaj.citation.annotation.Reference;
36 import org.openimaj.citation.annotation.ReferenceType;
37 import org.openimaj.image.FImage;
38 import org.openimaj.image.Image;
39 import org.openimaj.image.processing.convolution.FGaussianConvolve;
40 import org.openimaj.image.processing.convolution.FImageConvolveSeparable;
41 import org.openimaj.image.processor.SinglebandImageProcessor;
42 import org.openimaj.math.geometry.point.Point2d;
43 import org.openimaj.math.geometry.transforms.TransformUtilities;
44
45
46
47
48
49
50
51
52
53
54
55
56 @Reference(
57 type = ReferenceType.Article,
58 author = { "Morel, Jean-Michel", "Yu, Guoshen" },
59 title = "{ASIFT: A New Framework for Fully Affine Invariant Image Comparison}",
60 year = "2009",
61 journal = "SIAM J. Img. Sci.",
62 publisher = "Society for Industrial and Applied Mathematics")
63 public abstract class AffineSimulation<I extends Image<P, I> & SinglebandImageProcessor.Processable<Float, FImage, I>, P>
64 {
65 protected static final float PI = 3.141592654f;
66 protected static final float InitialAntiAliasingSigma = 1.6f;
67
68 private AffineSimulation() {
69 }
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 public static Point2d transformToOriginal(Point2d pt, int width, int height, float theta, float t) {
88 if (t == 1)
89 return pt;
90
91 return internalTransformToOriginal(pt, width, height, theta, t);
92 }
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108 public static Point2d transformToOriginal(Point2d pt, Image<?, ?> original, float theta, float t) {
109 if (t == 1)
110 return pt;
111
112 return internalTransformToOriginal(pt, original.getWidth(), original.getHeight(), theta, t);
113 }
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 public static Point2d transformToOriginal(Point2d pt, int width, int height, AffineParams params) {
130 return transformToOriginal(pt, width, height, params.theta, params.tilt);
131 }
132
133
134
135
136
137
138
139
140
141
142
143
144
145 public static Point2d transformToOriginal(Point2d pt, Image<?, ?> original, AffineParams params) {
146 return transformToOriginal(pt, original.getWidth(), original.getHeight(), params.theta, params.tilt);
147 }
148
149 protected static Point2d internalTransformToOriginal(Point2d pt, int width, int height, float Rtheta, float t1)
150 {
151 float x_ori, y_ori;
152 Rtheta = Rtheta * PI / 180;
153
154 if (Rtheta <= PI / 2) {
155 x_ori = 0;
156 y_ori = (float) ((width) * Math.sin(Rtheta) / t1);
157 } else {
158 x_ori = (float) (-(width) * Math.cos(Rtheta) / 1);
159 y_ori = (float) (((width) * Math.sin(Rtheta) + (height) * Math.sin(Rtheta - PI / 2)) / t1);
160 }
161
162 final float sin_Rtheta = (float) Math.sin(Rtheta);
163 final float cos_Rtheta = (float) Math.cos(Rtheta);
164
165 final Point2d ptout = pt.copy();
166
167
168
169
170
171
172 ptout.setX(pt.getX() - x_ori);
173 ptout.setY(pt.getY() - y_ori);
174
175
176 ptout.setX(ptout.getX() * 1);
177 ptout.setY(ptout.getY() * t1);
178
179
180
181
182
183
184 final float tx = cos_Rtheta * ptout.getX() - sin_Rtheta * ptout.getY();
185 final float ty = sin_Rtheta * ptout.getX() + cos_Rtheta * ptout.getY();
186
187 ptout.setX(tx);
188 ptout.setY(ty);
189
190 return ptout;
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212 public static <Q extends List<T>, T extends Point2d, I extends Image<?, I>> void transformToOriginal(Q points,
213 I original, float theta, float tilt)
214 {
215 final List<T> keys_to_remove = new ArrayList<T>();
216 float x_ori, y_ori;
217
218 if (theta <= PI / 2) {
219 x_ori = 0;
220 y_ori = (float) ((original.getWidth()) * Math.sin(theta) / tilt);
221 } else {
222 x_ori = (float) (-(original.getWidth()) * Math.cos(theta) / 1);
223 y_ori = (float) (((original.getWidth()) * Math.sin(theta) + (original.getHeight())
224 * Math.sin(theta - PI / 2)) / tilt);
225 }
226
227 final float sin_Rtheta = (float) Math.sin(theta);
228 final float cos_Rtheta = (float) Math.cos(theta);
229
230 for (final T k : points) {
231
232
233
234
235
236
237
238
239 k.setX(k.getX() - x_ori);
240 k.setY(k.getY() - y_ori);
241
242 k.setX(k.getX() * 1);
243 k.setY(k.getY() * tilt);
244
245
246
247
248
249 final float tx = cos_Rtheta * k.getX() - sin_Rtheta * k.getY();
250 final float ty = sin_Rtheta * k.getX() + cos_Rtheta * k.getY();
251
252 k.setX(tx);
253 k.setY(ty);
254
255 if (tx <= 0 || ty <= 0 || tx >= original.getWidth() || ty >= original.getHeight()) {
256 keys_to_remove.add(k);
257 }
258 }
259 points.removeAll(keys_to_remove);
260 }
261
262
263
264
265
266
267
268
269
270
271
272
273 public static <I extends Image<P, I> & SinglebandImageProcessor.Processable<Float, FImage, I>, P>
274 List<I> transformImage(I image, int numTilts)
275 {
276 if (numTilts < 1) {
277 throw new IllegalArgumentException("Number of tilts num_tilt should be equal or larger than 1.");
278 }
279
280 final List<I> transformed = new ArrayList<I>();
281 int num_rot1 = 0;
282
283 final int num_rot_t2 = 10;
284 final float t_min = 1;
285 final float t_k = (float) Math.sqrt(2);
286
287 for (int tt = 1; tt <= numTilts; tt++) {
288 final float t = t_min * (float) Math.pow(t_k, tt - 1);
289
290 if (t == 1) {
291 transformed.add(image.clone());
292 } else {
293 num_rot1 = Math.round(num_rot_t2 * t / 2);
294
295 if (num_rot1 % 2 == 1) {
296 num_rot1 = num_rot1 + 1;
297 }
298 num_rot1 = num_rot1 / 2;
299
300 final float delta_theta = PI / num_rot1;
301
302 for (int rr = 1; rr <= num_rot1; rr++) {
303 final float theta = delta_theta * (rr - 1);
304
305 transformed.add(transformImage(image, theta, t));
306 }
307 }
308 }
309
310 return transformed;
311 }
312
313
314
315
316
317
318
319
320
321
322
323
324 public static <I extends Image<P, I> & SinglebandImageProcessor.Processable<Float, FImage, I>, P> I transformImage(
325 I image, float theta, float t)
326 {
327 final float t1 = 1;
328 final float t2 = 1 / t;
329
330
331 final I image_rotated = ProjectionProcessor.project(image, TransformUtilities.rotationMatrix(-theta));
332
333
334
335 final float sigma_aa = InitialAntiAliasingSigma * t / 2;
336 image_rotated.processInplace(new FImageConvolveSeparable(null, FGaussianConvolve.makeKernel(sigma_aa)));
337
338
339 return ProjectionProcessor.project(image_rotated, TransformUtilities.scaleMatrix(t1, t2));
340 }
341
342
343
344
345
346
347
348
349
350
351 public static <I extends Image<P, I> & SinglebandImageProcessor.Processable<Float, FImage, I>, P> I transformImage(
352 I image, AffineParams params)
353 {
354 return transformImage(image, params.theta, params.tilt);
355 }
356
357 }