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_abstract.h: Abstract transformation superclass.
25 #ifndef __trans_abstract_h__
26 #define __trans_abstract_h__
32 #define M_PI 3.14159265358979323846
36 * Number of coefficients used in correcting barrel distortion.
39 #define BARREL_DEGREE 5
42 * Acceptable error for inverse barrel distortion, measured in scaled output
46 #define BARREL_INV_ERROR 0.01
48 struct trans_abstract
{
50 ale_pos bdc
[BARREL_DEGREE
]; // barrel-dist. coeffs.
51 unsigned int bdcnum
; // number of bdcs
55 unsigned int input_height
, input_width
;
57 virtual void specific_rescale(ale_pos factor
) = 0;
58 virtual void reset_memos() = 0;
59 virtual void specific_set_dimensions(const image
*im
) = 0;
67 trans_abstract
&operator=(const trans_abstract
&ta
) {
68 scale_factor
= ta
.scale_factor
;
69 input_width
= ta
.input_width
;
70 input_height
= ta
.input_height
;
74 assert (bdcnum
< BARREL_DEGREE
);
76 for (unsigned int d
= 0; d
< bdcnum
; d
++)
82 trans_abstract (const trans_abstract
&ta
) {
87 * Returns non-zero if the transformation might be non-Euclidean.
89 virtual int is_projective() const = 0;
95 ale_pos
scale() const {
100 * Get width of input image.
102 ale_pos
scaled_width() const {
103 return (input_width
* scale_factor
);
107 * Get unscaled width of input image.
109 unsigned int unscaled_width() const {
110 return (unsigned int) input_width
;
114 * Get height of input image;
116 ale_pos
scaled_height() const {
117 return (input_height
* scale_factor
);
121 * Get unscaled height of input image.
123 unsigned int unscaled_height() const {
124 return (unsigned int) input_height
;
128 * Barrel distortion radial component.
130 ale_pos
bdr(ale_pos r
) const {
131 assert (bdcnum
< BARREL_DEGREE
);
133 for (unsigned int d
= 0; d
< bdcnum
; d
++)
134 s
+= bdc
[d
] * (pow(r
, d
+ 2) - r
);
139 * Derivative of the barrel distortion radial component.
141 ale_pos
bdrd(ale_pos r
) const {
142 assert (bdcnum
< BARREL_DEGREE
);
144 for (unsigned int d
= 0; d
< bdcnum
; d
++)
145 s
+= bdc
[d
] * (pow(r
, d
+ 1) - 1);
152 struct point
bd(struct point p
) const {
154 point half_diag
= point(unscaled_height(), unscaled_width()) / 2;
158 ale_pos r
= p
.norm() / half_diag
.norm();
166 assert (!isnan(p
[0]) && !isnan(p
[1]));
172 * Barrel distortion inverse.
174 struct point
bdi(struct point p
) const {
176 point half_diag
= point(unscaled_height(), unscaled_width()) / 2;
180 ale_pos r
= p
.norm() / half_diag
.norm();
183 while (fabs(r
- bdr(s
)) * half_diag
.norm() > BARREL_INV_ERROR
)
184 s
+= (r
- bdr(s
)) / bdrd(s
);
192 assert (!isnan(p
[0]) && !isnan(p
[1]));
198 * Transformation sans barrel distortion
200 virtual struct point
pe(struct point p
) const = 0;
203 * Transformation inverse sans barrel distortion
205 virtual struct point
pei(struct point p
) const = 0;
208 * Map unscaled point p.
210 struct point
transform_unscaled(struct point p
) const {
217 * Barrel distortion correction followed by a projective/euclidean
220 struct point
transform_scaled(struct point p
) const {
221 return transform_unscaled(p
/ scale_factor
);
226 * operator() is the transformation operator.
228 struct point
operator()(struct point p
) {
234 * Map point p using the inverse of the transform into
235 * the unscaled image space.
237 struct point
unscaled_inverse_transform(struct point p
) const {
242 * Map point p using the inverse of the transform.
244 * Projective/euclidean inverse followed by barrel distortion.
246 struct point
scaled_inverse_transform(struct point p
) const {
247 assert (p
.defined());
248 point q
= unscaled_inverse_transform(p
);
250 q
[0] *= scale_factor
;
251 q
[1] *= scale_factor
;
257 * Calculate projective transformation parameters from a euclidean
260 virtual void eu_to_gpt() = 0;
263 * Modify a euclidean transform in the indicated manner.
265 virtual void eu_modify(int i1
, ale_pos diff
) = 0;
268 * Rotate about a given point in the original reference frame.
270 virtual void eu_rotate_about_scaled(point center
, ale_pos diff
) = 0;
273 * Modify all euclidean parameters at once.
275 virtual void eu_set(ale_pos eu
[3]) = 0;
278 * Get the specified euclidean parameter
280 virtual ale_pos
eu_get(int param
) const = 0;
283 * Modify a projective transform in the indicated manner.
285 virtual void gpt_modify(int i1
, int i2
, ale_pos diff
) = 0;
288 * Modify a projective transform according to the group operation.
290 virtual void gr_modify(int i1
, int i2
, ale_pos diff
) = 0;
293 * Modify all projective parameters at once.
295 virtual void gpt_set(point x
[4]) = 0;
297 virtual void gpt_set(point x1
, point x2
, point x3
, point x4
) = 0;
300 * Snap positional parameters to the specified resolution.
303 virtual void snap(ale_pos interval
) = 0;
306 * Get the specified projective parameter
308 virtual point
gpt_get(int point
) const = 0;
311 * Get the specified projective parameter
313 virtual ale_pos
gpt_get(int point
, int dim
) = 0;
316 * Check equality of transformation parameters.
318 virtual int operator==(const trans_abstract
&t
) const {
320 * Small tolerances (< 10^-6?) can cause odd errors,
321 * possibly due to float<->double conversion issues.
323 double zero_tolerance
= 0.01;
325 if (scale() != t
.scale())
328 if (is_projective() != t
.is_projective())
331 if (is_projective()) {
332 assert (t
.is_projective());
333 for (int i
= 0; i
< 4; i
++)
334 for (int d
= 0; d
< 2; d
++) {
335 double abs_difference
= fabs(gpt_get(i
)[d
] - t
.gpt_get(i
)[d
]);
337 if (abs_difference
> zero_tolerance
)
341 assert (!t
.is_projective());
342 for (int i
= 0; i
< 3; i
++) {
343 double abs_difference
= fabs(eu_get(i
) - t
.eu_get(i
));
345 if (abs_difference
> zero_tolerance
)
353 virtual int operator!=(const trans_abstract
&t
) const {
354 return !(operator==(t
));
359 * Translate by a given amount
361 virtual void translate(point p
) = 0;
364 * Rotate by a given amount about a given point.
366 virtual void rotate(point p
, ale_pos degrees
) = 0;
369 * Set the specified barrel distortion parameter.
371 void bd_set(unsigned int degree
, ale_pos value
) {
372 assert (degree
< bdcnum
);
377 * Set all barrel distortion parameters.
379 void bd_set(unsigned int degree
, ale_pos values
[BARREL_DEGREE
]) {
380 assert (degree
<= BARREL_DEGREE
);
382 for (unsigned int d
= 0; d
< degree
; d
++)
387 * Get all barrel distortion parameters.
389 void bd_get(ale_pos result
[BARREL_DEGREE
]) {
390 for (unsigned int d
= 0; d
< bdcnum
; d
++)
395 * Get the specified barrel distortion parameter.
397 ale_pos
bd_get(unsigned int degree
) {
398 assert (degree
< bdcnum
);
403 * Get the number of barrel distortion parameters.
405 unsigned int bd_count() {
410 * Get the maximum allowable number of barrel distortion parameters.
412 unsigned int bd_max() {
413 return BARREL_DEGREE
;
417 * Modify the specified barrel distortion parameter.
419 void bd_modify(unsigned int degree
, ale_pos diff
) {
420 assert (degree
< bdcnum
);
421 bd_set(degree
, bd_get(degree
) + diff
);
425 * Rescale a transform with a given factor.
427 void rescale(ale_pos factor
) {
428 specific_rescale(factor
);
429 scale_factor
*= factor
;
436 void set_domain(unsigned int new_height
, unsigned int new_width
) {
438 input_width
= new_width
;
439 input_height
= new_height
;
443 * Set the dimensions of the image.
445 void set_dimensions(const image
*im
) {
447 int new_height
= (int) im
->height();
448 int new_width
= (int) im
->width();
451 specific_set_dimensions(im
);
452 input_height
= new_height
;
453 input_width
= new_width
;
457 * Get the position and dimensions of a pixel P mapped from one
458 * coordinate system to another, using the forward transformation.
459 * This function uses scaled input coordinates.
461 void map_area(point p
, point
*q
, ale_pos d
[2]) {
464 * Determine the coordinates in the target frame for the source
465 * image pixel P and two adjacent source pixels.
468 (*q
) = transform_scaled(p
);
469 point q0
= transform_scaled(point(p
[0] + 1, p
[1]));
470 point q1
= transform_scaled(point(p
[0], p
[1] + 1));
473 * Calculate the distance between source image pixel and
474 * adjacent source pixels, measured in the coordinate system of
478 ale_pos ui
= fabs(q0
[0] - (*q
)[0]);
479 ale_pos uj
= fabs(q0
[1] - (*q
)[1]);
480 ale_pos vi
= fabs(q1
[0] - (*q
)[0]);
481 ale_pos vj
= fabs(q1
[1] - (*q
)[1]);
484 * We map the area of the source image pixel P onto the target
485 * frame as a rectangular area oriented on the target frame's
486 * axes. Note that this results in an area that may be the
487 * wrong shape or orientation.
489 * We define two estimates of the rectangle's dimensions below.
490 * For rotations of 0, 90, 180, or 270 degrees, max and sum are
491 * identical. For other orientations, sum is too large and max
492 * is too small. We use the mean of max and sum, which we then
493 * divide by two to obtain the distance between the center and
497 ale_pos maxi
= (ui
> vi
) ? ui
: vi
;
498 ale_pos maxj
= (uj
> vj
) ? uj
: vj
;
499 ale_pos sumi
= ui
+ vi
;
500 ale_pos sumj
= uj
+ vj
;
502 d
[0] = (maxi
+ sumi
) / 4;
503 d
[1] = (maxj
+ sumj
) / 4;
507 * Get the position and dimensions of a pixel P mapped from one
508 * coordinate system to another, using the forward transformation.
509 * This function uses unscaled input coordinates.
511 void map_area_unscaled(point p
, point
*q
, ale_pos d
[2]) {
514 * Determine the coordinates in the target frame for the source
515 * image pixel P and two adjacent source pixels.
518 (*q
) = transform_unscaled(p
);
519 point q0
= transform_unscaled(point(p
[0] + 1, p
[1]));
520 point q1
= transform_unscaled(point(p
[0], p
[1] + 1));
523 * Calculate the distance between source image pixel and
524 * adjacent source pixels, measured in the coordinate system of
528 ale_pos ui
= fabs(q0
[0] - (*q
)[0]);
529 ale_pos uj
= fabs(q0
[1] - (*q
)[1]);
530 ale_pos vi
= fabs(q1
[0] - (*q
)[0]);
531 ale_pos vj
= fabs(q1
[1] - (*q
)[1]);
534 * We map the area of the source image pixel P onto the target
535 * frame as a rectangular area oriented on the target frame's
536 * axes. Note that this results in an area that may be the
537 * wrong shape or orientation.
539 * We define two estimates of the rectangle's dimensions below.
540 * For rotations of 0, 90, 180, or 270 degrees, max and sum are
541 * identical. For other orientations, sum is too large and max
542 * is too small. We use the mean of max and sum, which we then
543 * divide by two to obtain the distance between the center and
547 ale_pos maxi
= (ui
> vi
) ? ui
: vi
;
548 ale_pos maxj
= (uj
> vj
) ? uj
: vj
;
549 ale_pos sumi
= ui
+ vi
;
550 ale_pos sumj
= uj
+ vj
;
552 d
[0] = (maxi
+ sumi
) / 4;
553 d
[1] = (maxj
+ sumj
) / 4;
557 * Get the position and dimensions of a pixel P mapped from one
558 * coordinate system to another, using the inverse transformation. If
559 * SCALE_FACTOR is not equal to one, divide out the scale factor to
560 * obtain unscaled coordinates. This method is very similar to the
561 * map_area method above.
563 void unscaled_map_area_inverse(point p
, point
*q
, ale_pos d
[2]) {
566 * Determine the coordinates in the target frame for the source
567 * image pixel P and two adjacent source pixels.
570 (*q
) = scaled_inverse_transform(p
);
571 point q0
= scaled_inverse_transform(point(p
[0] + 1, p
[1]));
572 point q1
= scaled_inverse_transform(point(p
[0], p
[1] + 1));
576 * Calculate the distance between source image pixel and
577 * adjacent source pixels, measured in the coordinate system of
581 ale_pos ui
= fabs(q0
[0] - (*q
)[0]);
582 ale_pos uj
= fabs(q0
[1] - (*q
)[1]);
583 ale_pos vi
= fabs(q1
[0] - (*q
)[0]);
584 ale_pos vj
= fabs(q1
[1] - (*q
)[1]);
587 * We map the area of the source image pixel P onto the target
588 * frame as a rectangular area oriented on the target frame's
589 * axes. Note that this results in an area that may be the
590 * wrong shape or orientation.
592 * We define two estimates of the rectangle's dimensions below.
593 * For rotations of 0, 90, 180, or 270 degrees, max and sum are
594 * identical. For other orientations, sum is too large and max
595 * is too small. We use the mean of max and sum, which we then
596 * divide by two to obtain the distance between the center and
600 ale_pos maxi
= (ui
> vi
) ? ui
: vi
;
601 ale_pos maxj
= (uj
> vj
) ? uj
: vj
;
602 ale_pos sumi
= ui
+ vi
;
603 ale_pos sumj
= uj
+ vj
;
605 d
[0] = (maxi
+ sumi
) / 4;
606 d
[1] = (maxj
+ sumj
) / 4;
608 if (scale_factor
!= 1) {
609 d
[0] /= scale_factor
;
610 d
[1] /= scale_factor
;
611 (*q
)[0] /= scale_factor
;
612 (*q
)[1] /= scale_factor
;
617 * Modify all projective parameters at once. Accommodate bugs in the
618 * version 0 transformation file handler (ALE versions 0.4.0p1 and
619 * earlier). This code is only called when using a transformation data
620 * file created with an old version of ALE.
622 virtual void gpt_v0_set(point x
[4]) = 0;
625 * Modify all euclidean parameters at once. Accommodate bugs in the
626 * version 0 transformation file handler (ALE versions 0.4.0p1 and
627 * earlier). This code is only called when using a transformation data
628 * file created with an old version of ALE.
630 virtual void eu_v0_set(ale_pos eu
[3]) = 0;
632 virtual void debug_output() = 0;
634 virtual ~trans_abstract() {