1 // Copyright 2002, 2004, 2007 David Hilvert <dhilvert@auricle.dyndns.org>,
2 // <dhilvert@ugcs.caltech.edu>
4 /* This file is part of the Anti-Lamenessing Engine.
6 The Anti-Lamenessing Engine is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 The Anti-Lamenessing Engine is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with the Anti-Lamenessing Engine; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * trans_multi.h: Represent multiple transformations, affecting different
26 #ifndef __trans_multi_h__
27 #define __trans_multi_h__
29 #include "trans_abstract.h"
30 #include "trans_single.h"
32 struct trans_multi
: public trans_abstract
{
34 struct multi_coordinate
{
40 int operator<(const multi_coordinate
&mc
) const {
41 if (degree
< mc
.degree
42 || degree
== mc
.degree
&& y
< mc
.y
43 || degree
== mc
.degree
&& y
== mc
.y
&& x
< mc
.x
)
51 static ale_pos _multi_decomp
;
53 typedef unsigned int index_t
;
55 std::vector
<trans_single
> trans_stack
;
56 std::vector
<multi_coordinate
> coord_stack
;
57 std::map
<multi_coordinate
, index_t
> coordinate_map
;
60 index_t current_element
;
62 index_t orig_ref_height
, orig_ref_width
;
63 index_t cur_ref_height
, cur_ref_width
;
67 index_t
*spatio_elem_map
;
68 index_t
*spatio_elem_map_r
;
71 assert (trans_stack
.size() > 0);
73 if (++current_element
== trans_stack
.size())
74 trans_stack
.push_back(trans_stack
.back());
77 trans_multi() : trans_stack() {
84 spatio_elem_map
= NULL
;
85 spatio_elem_map_r
= NULL
;
90 static void set_md(double d
) {
95 * Calculate euclidean identity transform for a given image.
97 static struct trans_multi
eu_identity(const image
*i
= NULL
, ale_pos scale_factor
= 1) {
105 r
.input_width
= i
? i
->width() : 2;
106 r
.input_height
= i
? i
->height() : 2;
107 r
.scale_factor
= scale_factor
;
108 r
.trans_stack
.push_back(trans_single::eu_identity(i
, scale_factor
));
109 r
.coord_stack
.push_back(mc
);
110 r
.coordinate_map
[mc
] = r
.trans_stack
.size() - 1;
111 r
.current_element
= 0;
116 * Generate an array of identity transformations.
118 static trans_multi
*new_eu_identity_array(unsigned int size
) {
119 trans_multi
*result
= new trans_multi
[size
];
120 for (unsigned int i
= 0; i
< size
; i
++)
121 result
[i
] = eu_identity();
126 * Calculate projective transformation parameters from a euclidean
130 for (unsigned int t
= 0; t
< trans_stack
.size(); t
++)
131 trans_stack
[t
].eu_to_gpt();
135 * Calculate projective identity transform for a given image.
137 static trans_multi
gpt_identity(const image
*i
, ale_pos scale_factor
) {
138 struct trans_multi r
= eu_identity(i
, scale_factor
);
143 trans_multi
&operator=(const trans_multi
&tm
) {
144 this->trans_abstract::operator=(*((trans_abstract
*) &tm
));
146 trans_stack
= tm
.trans_stack
;
147 coord_stack
= tm
.coord_stack
;
148 coordinate_map
= tm
.coordinate_map
;
150 use_multi
= tm
.use_multi
;
151 current_element
= tm
.current_element
;
153 orig_ref_height
= tm
.orig_ref_height
;
154 orig_ref_width
= tm
.orig_ref_width
;
155 cur_ref_height
= tm
.cur_ref_height
;
156 cur_ref_width
= tm
.cur_ref_width
;
157 cur_offset
= tm
.cur_offset
;
158 orig_offset
= tm
.orig_offset
;
160 free(spatio_elem_map
);
161 free(spatio_elem_map_r
);
162 spatio_elem_map
= NULL
;
163 spatio_elem_map_r
= NULL
;
165 size_t cur_size
= cur_ref_width
* cur_ref_height
* sizeof(index_t
);
167 if (cur_size
> 0 && tm
.spatio_elem_map
) {
168 spatio_elem_map
= (index_t
*) malloc(cur_size
);
169 assert (spatio_elem_map
);
170 memcpy(spatio_elem_map
, tm
.spatio_elem_map
, cur_size
);
173 cur_size
= input_height
* input_width
* sizeof(index_t
);
174 if (cur_size
> 0 && tm
.spatio_elem_map_r
) {
175 spatio_elem_map_r
= (index_t
*) malloc(cur_size
);
176 assert (spatio_elem_map_r
);
177 memcpy(spatio_elem_map_r
, tm
.spatio_elem_map_r
, cur_size
);
183 trans_multi(const trans_multi
&tm
) : trans_stack() {
184 spatio_elem_map
= NULL
;
185 spatio_elem_map_r
= NULL
;
190 free(spatio_elem_map
);
191 free(spatio_elem_map_r
);
194 trans_single
get_element(index_t index
) const {
195 assert (index
< trans_stack
.size());
197 return trans_stack
[index
];
200 trans_single
get_element(multi_coordinate m
) {
201 assert(coordinate_map
.count(m
));
202 index_t index
= coordinate_map
[m
];
204 return get_element(index
);
207 index_t
get_index(multi_coordinate m
) {
208 assert(coordinate_map
.count(m
));
209 return coordinate_map
[m
];
212 int exists(multi_coordinate m
) {
213 return coordinate_map
.count(m
);
216 trans_single
get_current_element() const {
217 return get_element(current_element
);
220 void set_element(index_t index
, trans_single t
) {
221 assert (index
< trans_stack
.size());
223 trans_stack
[index
] = t
;
226 void set_current_element(trans_single t
) {
227 set_element(current_element
, t
);
230 void set_current_element(const trans_multi
&t
) {
231 set_element(current_element
, t
.get_current_element());
234 index_t
get_current_index() const {
235 return current_element
;
238 multi_coordinate
get_current_coordinate() const {
239 return coord_stack
[current_element
];
242 multi_coordinate
get_coordinate(index_t i
) const {
243 assert(i
< trans_stack
.size());
244 return coord_stack
[i
];
247 void set_current_index(index_t i
) {
248 assert (i
< trans_stack
.size());
253 * Set the bounds of the reference image after incorporation
254 * of the original frame.
256 void set_original_bounds(const image
*i
) {
257 assert (orig_ref_width
== 0);
258 assert (orig_ref_height
== 0);
260 orig_ref_height
= i
->height();
261 orig_ref_width
= i
->width();
262 orig_offset
= i
->offset();
264 assert (orig_ref_width
!= 0);
265 assert (orig_ref_height
!= 0);
268 static multi_coordinate
parent_mc(multi_coordinate mc
) {
269 multi_coordinate result
;
271 assert (mc
.degree
> 0);
273 if (mc
.degree
== 1) {
281 result
.degree
= mc
.degree
- 1;
282 result
.x
= (int) floor((double) mc
.x
/ (double) 2);
283 result
.y
= (int) floor((double) mc
.y
/ (double) 2);
288 index_t
parent_index(index_t i
) {
289 multi_coordinate mc
= coord_stack
[i
];
290 multi_coordinate mcp
= parent_mc(mc
);
291 index_t result
= coordinate_map
[mcp
];
297 * Set the bounds of the reference image after incorporation
298 * of the most recent frame.
300 void set_current_bounds(const image
*i
) {
302 free(spatio_elem_map
);
303 free(spatio_elem_map_r
);
304 spatio_elem_map
= NULL
;
305 spatio_elem_map_r
= NULL
;
307 cur_ref_height
= i
->height();
308 cur_ref_width
= i
->width();
309 cur_offset
= i
->offset();
313 orig_ref_height
/ div
>= _multi_decomp
314 && orig_ref_width
/ div
>= _multi_decomp
315 && _multi_decomp
> 0;
318 ale_pos height_scale
= orig_ref_height
/ div
;
319 ale_pos width_scale
= orig_ref_width
/ div
;
321 for (int i
= floor((cur_offset
[0] - orig_offset
[0]) / height_scale
);
322 i
< ceil((cur_offset
[0] - orig_offset
[0] + cur_ref_height
) / height_scale
);
324 for (int j
= floor((cur_offset
[1] - orig_offset
[1]) / width_scale
);
325 j
< ceil((cur_offset
[1] - orig_offset
[1] + cur_ref_width
) / width_scale
);
333 if (!coordinate_map
.count(c
)) {
334 multi_coordinate parent
= parent_mc(c
);
335 assert (coordinate_map
.count(parent
));
336 trans_stack
.push_back(trans_stack
[coordinate_map
[parent
]]);
337 coord_stack
.push_back(c
);
338 coordinate_map
[c
] = trans_stack
.size() - 1;
344 index_t
stack_depth() const {
345 return trans_stack
.size();
348 struct elem_bounds_t
{
349 ale_pos imin
, imax
, jmin
, jmax
;
352 elem_bounds_t
elem_bounds() const {
353 elem_bounds_t result
;
355 result
.imin
= cur_offset
[0] - orig_offset
[0];
356 result
.imax
= result
.imin
+ cur_ref_height
;
357 result
.jmin
= cur_offset
[1] - orig_offset
[1];
358 result
.jmax
= result
.jmin
+ cur_ref_width
;
360 if (current_element
> 0) {
361 multi_coordinate mc
= coord_stack
[current_element
];
363 ale_pos height_scale
= orig_ref_height
/ pow(2, mc
.degree
);
364 ale_pos width_scale
= orig_ref_width
/ pow(2, mc
.degree
);
366 if (height_scale
* mc
.y
> result
.imin
)
367 result
.imin
= height_scale
* mc
.y
;
368 if (height_scale
* (mc
.y
+ 1) < result
.imax
)
369 result
.imax
= height_scale
* (mc
.y
+ 1);
370 if (width_scale
* mc
.x
> result
.jmin
)
371 result
.jmin
= width_scale
* mc
.x
;
372 if (width_scale
* (mc
.x
+ 1) < result
.jmax
)
373 result
.jmax
= width_scale
* (mc
.x
+ 1);
376 result
.imin
-= cur_offset
[0] - orig_offset
[0];
377 result
.imax
-= cur_offset
[0] - orig_offset
[0];
378 result
.jmin
-= cur_offset
[1] - orig_offset
[1];
379 result
.jmax
-= cur_offset
[1] - orig_offset
[1];
382 result
.imin
/= cur_ref_height
;
383 result
.imax
/= cur_ref_height
;
384 result
.jmin
/= cur_ref_width
;
385 result
.jmax
/= cur_ref_width
;
390 void set_multi(const image
*cur_ref
, const image
*input
) {
391 assert(use_multi
== 0);
392 assert(spatio_elem_map
== NULL
);
393 assert(spatio_elem_map_r
== NULL
);
396 spatio_elem_map
= (index_t
*) calloc(
397 cur_ref_height
* cur_ref_width
, sizeof(index_t
));
398 assert(spatio_elem_map
);
400 spatio_elem_map_r
= (index_t
*) calloc(
401 input_height
* input_width
, sizeof(index_t
));
402 assert(spatio_elem_map_r
);
404 for (unsigned int i
= 0; i
< cur_ref_height
; i
++)
405 for (unsigned int j
= 0; j
< cur_ref_width
; j
++) {
406 pixel rp
= cur_ref
->get_pixel(i
, j
);
408 for (d
= 1, div
= 2; ; d
++, div
*= 2) {
409 ale_pos height_scale
= orig_ref_height
/ div
;
410 ale_pos width_scale
= orig_ref_width
/ div
;
413 c
.y
= floor((cur_offset
[0] - orig_offset
[0] + i
) / height_scale
);
414 c
.x
= floor((cur_offset
[1] - orig_offset
[1] + j
) / width_scale
);
415 if (!coordinate_map
.count(c
))
417 index_t index
= coordinate_map
[c
];
418 trans_single t
= get_element(index
);
419 point p
= t
.scaled_inverse_transform(point(cur_offset
[0] + i
,
421 if (!input
->in_bounds(p
))
424 trans_single s
= get_element(spatio_elem_map
[cur_ref_width
* i
+ j
]);
426 point q
= s
.scaled_inverse_transform(point(cur_offset
[0] + i
,
429 if (input
->in_bounds(q
)) {
430 pixel ip1
= input
->get_bl(p
);
431 pixel ip0
= input
->get_bl(q
);
433 ale_real diff1
= (ip1
- rp
).norm();
434 ale_real diff0
= (ip0
- rp
).norm();
437 spatio_elem_map
[cur_ref_width
* i
+ j
] = index
;
443 if (ii
< 0 || (unsigned int) ii
>= input_height
444 || jj
< 0 || (unsigned int) jj
>= input_width
)
447 trans_single u
= get_element(spatio_elem_map_r
[input_width
* ii
+ jj
]);
448 point r
= u
.transform_scaled(p
);
450 if (cur_ref
->in_bounds(r
- cur_offset
)) {
451 pixel ip1
= input
->get_bl(p
);
452 pixel rp0
= cur_ref
->get_bl(r
- cur_offset
);
454 ale_real diff1
= (ip1
- rp
).norm();
455 ale_real diff0
= (ip1
- rp0
).norm();
458 spatio_elem_map_r
[input_width
* ii
+ jj
] = index
;
465 * Returns non-zero if the transformation might be non-Euclidean.
467 int is_projective() const {
468 return trans_stack
.front().is_projective();
472 * Projective/Euclidean transformations
474 struct point
pe(struct point p
) const {
476 return trans_stack
.front().pe(p
);
481 if (ii
< 0 || (unsigned int) ii
>= input_height
482 || jj
< 0 || (unsigned int) jj
>= input_width
)
483 return trans_stack
[0].pe(p
);
485 return trans_stack
[spatio_elem_map_r
[input_width
* ii
+ jj
]].pe(p
);
489 * Inverse transformations
491 struct point
pei(struct point p
) const {
493 return trans_stack
.front().pei(p
);
498 if (i
< 0 || (unsigned int) i
>= cur_ref_height
499 || j
< 0 || (unsigned int) j
>= cur_ref_width
)
500 return trans_stack
[0].pei(p
);
502 return trans_stack
[spatio_elem_map
[cur_ref_width
* i
+ j
]].pei(p
);
507 * Modify a euclidean transform in the indicated manner.
509 void eu_modify(int i1
, ale_pos diff
) {
510 trans_stack
[current_element
].eu_modify(i1
, diff
);
514 * Rotate about a given point in the original reference frame.
516 void eu_rotate_about_scaled(point center
, ale_pos diff
) {
517 trans_stack
[current_element
].eu_rotate_about_scaled(center
, diff
);
521 * Modify all euclidean parameters at once.
523 void eu_set(ale_pos eu
[3]) {
524 trans_stack
[current_element
].eu_set(eu
);
528 * Get the specified euclidean parameter
530 ale_pos
eu_get(int param
) const {
531 return trans_stack
[current_element
].eu_get(param
);
535 * Modify a projective transform in the indicated manner.
537 void gpt_modify(int i1
, int i2
, ale_pos diff
) {
538 trans_stack
[current_element
].gpt_modify(i1
, i2
, diff
);
542 * Modify a projective transform according to the group operation.
544 void gr_modify(int i1
, int i2
, ale_pos diff
) {
545 trans_stack
[current_element
].gr_modify(i1
, i2
, diff
);
549 * Modify all projective parameters at once.
551 void gpt_set(point x
[4]) {
552 trans_stack
[current_element
].gpt_set(x
);
555 void gpt_set(point x1
, point x2
, point x3
, point x4
) {
556 trans_stack
[current_element
].gpt_set(x1
, x2
, x3
, x4
);
559 void snap(ale_pos interval
) {
560 trans_stack
[current_element
].snap(interval
);
564 * Get the specified projective parameter
566 point
gpt_get(int point
) const {
567 return trans_stack
[current_element
].gpt_get(point
);
571 * Get the specified projective parameter
573 ale_pos
gpt_get(int point
, int dim
) {
574 return trans_stack
[current_element
].gpt_get(point
, dim
);
578 * Translate by a given amount
580 void translate(point p
) {
581 trans_stack
[current_element
].translate(p
);
585 * Rotate by a given amount about a given point.
587 void rotate(point p
, ale_pos degrees
) {
588 trans_stack
[current_element
].rotate(p
, degrees
);
592 for (unsigned int t
= 0; t
< trans_stack
.size(); t
++)
593 trans_stack
[t
].reset_memos();
597 * Rescale a transform with a given factor.
599 void specific_rescale(ale_pos factor
) {
602 * Ensure that no maps exist.
605 assert (use_multi
== 0);
606 assert (spatio_elem_map
== NULL
);
607 assert (spatio_elem_map_r
== NULL
);
609 trans_stack
[current_element
].rescale(factor
);
613 * Set the dimensions of the image.
615 void specific_set_dimensions(const image
*im
) {
616 for (unsigned int t
= 0; t
< trans_stack
.size(); t
++)
617 trans_stack
[t
].set_dimensions(im
);
621 * Modify all projective parameters at once. Accommodate bugs in the
622 * version 0 transformation file handler (ALE versions 0.4.0p1 and
623 * earlier). This code is only called when using a transformation data
624 * file created with an old version of ALE.
626 void gpt_v0_set(point x
[4]) {
627 trans_stack
[current_element
].gpt_v0_set(x
);
631 * Modify all euclidean parameters at once. Accommodate bugs in the
632 * version 0 transformation file handler (ALE versions 0.4.0p1 and
633 * earlier). This code is only called when using a transformation data
634 * file created with an old version of ALE.
636 void eu_v0_set(ale_pos eu
[3]) {
637 trans_stack
[current_element
].eu_v0_set(eu
);
640 void debug_output() {
641 for (unsigned int t
= 0; t
< trans_stack
.size(); t
++)
642 trans_stack
[t
].debug_output();