Add preliminary code for stream evaluation.
[Ale.git] / d2 / transformation.h
blob3818970d27535949770cd60700c38d2d1fdd64d0
1 // Copyright 2002, 2004 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 2 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 * transformation.h: Represent transformations of the kind q = c(b^-1(p)),
23 * where p is a point in the source coordinate system, q is a point in the
24 * target coordinate system, b^-1 is a transformation correcting barrel
25 * distortion, and c is a transformation of projective or Euclidean type.
26 * (Note that ^-1 in this context indicates the function inverse rather than
27 * the exponential.)
30 #ifndef __transformation_h__
31 #define __transformation_h__
33 #include "image.h"
34 #include "point.h"
36 #ifndef M_PI
37 #define M_PI 3.14159265358979323846
38 #endif
41 * Number of coefficients used in correcting barrel distortion.
44 #define BARREL_DEGREE 5
47 * Acceptable error for inverse barrel distortion, measured in scaled output
48 * pixels.
51 #define BARREL_INV_ERROR 0.01
54 * transformation: a structure to describe a transformation of kind q =
55 * c(b^-1(p)), where p is a point in the source coordinate system, q is a point
56 * in the target coordinate system, b^-1 is a transformation correcting barrel
57 * distortion, and c is a projective or Euclidean transformation. (Note that
58 * ^-1 in this case indicates a function inverse, not exponentiation.) Data
59 * elements are divided into those describing barrel distortion correction and
60 * those describing projective/Euclidean transformations.
62 * Barrel distortion correction estimates barrel distortion using polynomial
63 * functions of distance from the center of an image, following (roughly) the
64 * example set by Helmut Dersch in his PanoTools software:
66 * http://www.path.unimelb.edu.au/~dersch/barrel/barrel.html
68 * Projective transformation data member names roughly correspond to a typical
69 * treatment of projective transformations from:
71 * Heckbert, Paul. "Projective Mappings for Image Warping." Excerpted
72 * from his Master's Thesis (UC Berkeley, 1989). 1995.
74 * http://www.cs.cmu.edu/afs/cs/project/classes-ph/862.95/www/notes/proj.ps
76 * For convenience, Heckbert's 'x' and 'y' are noted here numerically by '0'
77 * and '1', respectively. 'x0' is denoted 'x[0][0]'; 'y0' is 'x[0][1]'.
79 * eu[i] are the parameters for euclidean transformations.
81 * We consider points to be transformed as homogeneous coordinate vectors
82 * multiplied on the right of the transformation matrix, and so we consider the
83 * transformation matrix as
85 * - -
86 * | a b c |
87 * | d e f |
88 * | g h i |
89 * - -
91 * where element i is always equal to 1.
94 struct transformation {
95 private:
96 unsigned int input_height, input_width;
97 point x[4];
98 ale_pos eu[3];
99 mutable ale_pos a, b, c, d, e, f, g, h; // matrix
100 mutable ale_pos _a, _b, _c, _d, _e, _f, _g, _h; // matrix inverse
101 ale_pos bdc[BARREL_DEGREE]; // barrel-dist. coeffs.
102 unsigned int bdcnum; // number of bdcs
103 int _is_projective;
104 mutable int resultant_memo;
105 mutable int resultant_inverse_memo;
106 ale_pos scale_factor;
109 * Calculate resultant matrix values.
111 void resultant() const {
114 * If we already know the answers, don't bother calculating
115 * them again.
118 if (resultant_memo)
119 return;
121 if (_is_projective) {
124 * Calculate resultant matrix values for a general
125 * projective transformation given that we are mapping
126 * from the source domain of dimension input_height *
127 * input_width to a specified arbitrary quadrilateral.
128 * Follow the calculations outlined in the document by
129 * Paul Heckbert cited above for the case in which the
130 * source domain is a unit square and then divide to
131 * correct for the scale factor in each dimension.
135 * First, perform calculations as outlined in Heckbert.
138 ale_pos delta_01 = x[1][0] - x[2][0];
139 ale_pos delta_02 = x[3][0] - x[2][0];
140 ale_pos sigma_0 = x[0][0] - x[1][0] + x[2][0] - x[3][0];
141 ale_pos delta_11 = x[1][1] - x[2][1];
142 ale_pos delta_12 = x[3][1] - x[2][1];
143 ale_pos sigma_1 = x[0][1] - x[1][1] + x[2][1] - x[3][1];
145 g = (sigma_0 * delta_12 - sigma_1 * delta_02)
146 / (delta_01 * delta_12 - delta_11 * delta_02);
148 h = (delta_01 * sigma_1 - delta_11 * sigma_0 )
149 / (delta_01 * delta_12 - delta_11 * delta_02);
151 a = (x[1][0] - x[0][0] + g * x[1][0]);
152 b = (x[3][0] - x[0][0] + h * x[3][0]);
153 c = x[0][0];
155 d = (x[1][1] - x[0][1] + g * x[1][1]);
156 e = (x[3][1] - x[0][1] + h * x[3][1]);
157 f = x[0][1];
160 * Finish by scaling so that our transformation maps
161 * from a rectangle of width and height matching the
162 * width and height of the input image.
165 a /= input_height;
166 b /= input_width;
167 d /= input_height;
168 e /= input_width;
169 g /= input_height;
170 h /= input_width;
172 } else {
175 * Calculate matrix values for a euclidean
176 * transformation.
178 * We want to translate the image center by (eu[0],
179 * eu[1]) and rotate the image about the center by
180 * eu[2] degrees. This is equivalent to the following
181 * sequence of affine transformations applied to the
182 * point to be transformed:
184 * translate by (-h/2, -w/2)
185 * rotate by eu[2] degrees about the origin
186 * translate by (h/2, w/2)
187 * translate by (eu[0], eu[1])
189 * The matrix assigned below represents the result of
190 * combining all of these transformations. Matrix
191 * elements g and h are always zero in an affine
192 * transformation.
195 ale_pos theta = eu[2] * M_PI / 180;
197 a = cos(theta) * scale_factor;
198 b = sin(theta) * scale_factor;
199 c = 0.5 * (input_height * (scale_factor - a) - input_width * b) + eu[0] * scale_factor;
200 d = -b;
201 e = a;
202 f = 0.5 * (input_height * b + input_width * (scale_factor - a)) + eu[1] * scale_factor;
203 g = 0;
204 h = 0;
207 resultant_memo = 1;
211 * Calculate the inverse transform matrix values.
213 void resultant_inverse () const {
216 * If we already know the answers, don't bother calculating
217 * them again.
220 if (resultant_inverse_memo)
221 return;
223 resultant();
226 * For projective transformations, we calculate
227 * the inverse of the forward transformation
228 * matrix.
231 ale_pos scale = a * e - b * d;
233 _a = (e * 1 - f * h) / scale;
234 _b = (h * c - 1 * b) / scale;
235 _c = (b * f - c * e) / scale;
236 _d = (f * g - d * 1) / scale;
237 _e = (1 * a - g * c) / scale;
238 _f = (c * d - a * f) / scale;
239 _g = (d * h - e * g) / scale;
240 _h = (g * b - h * a) / scale;
242 resultant_inverse_memo = 1;
245 public:
248 * Returns non-zero if the transformation might be non-Euclidean.
250 int is_projective() {
251 return _is_projective;
255 * Get scale factor.
258 ale_pos scale() const {
259 return scale_factor;
263 * Get width of input image.
265 ale_pos scaled_width() const {
266 return (input_width * scale_factor);
270 * Get unscaled width of input image.
272 unsigned int unscaled_width() const {
273 return (unsigned int) input_width;
277 * Get height of input image;
279 ale_pos scaled_height() const {
280 return (input_height * scale_factor);
284 * Get unscaled height of input image.
286 unsigned int unscaled_height() const {
287 return (unsigned int) input_height;
291 * Barrel distortion radial component.
293 ale_pos bdr(ale_pos r) const {
294 ale_pos s = r;
295 for (unsigned int d = 0; d < bdcnum; d++)
296 s += bdc[d] * (pow(r, d + 2) - r);
297 return s;
301 * Derivative of the barrel distortion radial component.
303 ale_pos bdrd(ale_pos r) const {
304 ale_pos s = 1;
305 for (unsigned int d = 0; d < bdcnum; d++)
306 s += bdc[d] * (pow(r, d + 1) - 1);
307 return s;
311 * Barrel distortion.
313 struct point bd(struct point p) const {
314 if (bdcnum > 0) {
315 point half_diag = point(unscaled_height(), unscaled_width()) / 2;
317 p -= half_diag;
319 ale_pos r = p.norm() / half_diag.norm();
321 if (r > 0.00001)
322 p *= bdr(r)/r;
324 p += half_diag;
327 assert (!isnan(p[0]) && !isnan(p[1]));
329 return p;
333 * Barrel distortion inverse.
335 struct point bdi(struct point p) const {
336 if (bdcnum > 0) {
337 point half_diag = point(unscaled_height(), unscaled_width()) / 2;
339 p -= half_diag;
341 ale_pos r = p.norm() / half_diag.norm();
342 ale_pos s = r;
344 while (fabs(r - bdr(s)) * half_diag.norm() > BARREL_INV_ERROR)
345 s += (r - bdr(s)) / bdrd(s);
347 if (r > 0.0001)
348 p *= s / r;
350 p += half_diag;
353 assert (!isnan(p[0]) && !isnan(p[1]));
355 return p;
359 * Projective/Euclidean transformation
361 struct point pe(struct point p) const {
362 struct point result;
364 resultant();
366 result[0] = (a * p[0] + b * p[1] + c)
367 / (g * p[0] + h * p[1] + 1);
368 result[1] = (d * p[0] + e * p[1] + f)
369 / (g * p[0] + h * p[1] + 1);
371 return result;
375 * Projective/Euclidean inverse
377 struct point pei(struct point p) const {
378 struct point result;
380 resultant_inverse();
382 result[0] = (_a * p[0] + _b * p[1] + _c)
383 / (_g * p[0] + _h * p[1] + 1);
384 result[1] = (_d * p[0] + _e * p[1] + _f)
385 / (_g * p[0] + _h * p[1] + 1);
387 return result;
392 * Map unscaled point p.
394 struct point transform_unscaled(struct point p) const {
395 return pe(bdi(p));
399 * Transform point p.
401 * Barrel distortion correction followed by a projective/euclidean
402 * transformation.
404 struct point transform_scaled(struct point p) const {
405 return transform_unscaled(p / scale_factor);
408 #if 0
410 * operator() is the transformation operator.
412 struct point operator()(struct point p) {
413 return transform(p);
415 #endif
418 * Map point p using the inverse of the transform into
419 * the unscaled image space.
421 struct point unscaled_inverse_transform(struct point p) const {
422 return bd(pei(p));
426 * Map point p using the inverse of the transform.
428 * Projective/euclidean inverse followed by barrel distortion.
430 struct point scaled_inverse_transform(struct point p) const {
431 point q = unscaled_inverse_transform(p);
433 q[0] *= scale_factor;
434 q[1] *= scale_factor;
436 return q;
440 * Calculate projective transformation parameters from a euclidean
441 * transformation.
443 void eu_to_gpt() {
445 assert(!_is_projective);
447 x[0] = transform_unscaled(point( 0 , 0 ) );
448 x[1] = transform_unscaled(point( input_height, 0 ) );
449 x[2] = transform_unscaled(point( input_height, input_width ) );
450 x[3] = transform_unscaled(point( 0 , input_width ) );
452 resultant_memo = 0;
453 resultant_inverse_memo = 0;
455 _is_projective = 1;
459 * Calculate euclidean identity transform for a given image.
461 static struct transformation eu_identity(const image *i = NULL, ale_pos scale_factor = 1) {
462 struct transformation r;
464 r.resultant_memo = 0;
465 r.resultant_inverse_memo = 0;
467 r.eu[0] = 0;
468 r.eu[1] = 0;
469 r.eu[2] = 0;
470 r.input_width = i ? i->width() : 2;
471 r.input_height = i ? i->height() : 2;
472 r.scale_factor = scale_factor;
474 r._is_projective = 0;
476 r.bdcnum = 0;
478 return r;
482 * Calculate projective identity transform for a given image.
484 static transformation gpt_identity(const image *i, ale_pos scale_factor) {
485 struct transformation r = eu_identity(i, scale_factor);
487 r.eu_to_gpt();
489 return r;
493 * Modify a euclidean transform in the indicated manner.
495 void eu_modify(int i1, ale_pos diff) {
497 assert(!_is_projective);
499 resultant_memo = 0;
500 resultant_inverse_memo = 0;
502 if (i1 < 2)
503 eu[i1] += diff / scale_factor;
504 else
505 eu[i1] += diff;
510 * Modify all euclidean parameters at once.
512 void eu_set(ale_pos eu[3]) {
514 resultant_memo = 0;
515 resultant_inverse_memo = 0;
517 this->eu[0] = eu[0] / scale_factor;
518 this->eu[1] = eu[1] / scale_factor;
519 this->eu[2] = eu[2];
521 if (_is_projective) {
522 _is_projective = 0;
523 eu_to_gpt();
529 * Get the specified euclidean parameter
531 ale_pos eu_get(int param) {
532 assert (!_is_projective);
533 assert (param >= 0);
534 assert (param < 3);
536 if (param < 2)
537 return eu[param] * scale_factor;
538 else
539 return eu[param];
543 * Modify a projective transform in the indicated manner.
545 void gpt_modify(int i1, int i2, ale_pos diff) {
547 assert (_is_projective);
549 resultant_memo = 0;
550 resultant_inverse_memo = 0;
552 x[i2][i1] += diff;
557 * Modify a projective transform according to the group operation.
559 void gr_modify(int i1, int i2, ale_pos diff) {
560 assert (_is_projective);
561 assert (i1 == 0 || i1 == 1);
563 point diff_vector = (i1 == 0)
564 ? point(diff, 0)
565 : point(0, diff);
567 transformation t = *this;
569 t.resultant_memo = 0;
570 t.resultant_inverse_memo = 0;
571 t.input_height = (unsigned int) scaled_height();
572 t.input_width = (unsigned int) scaled_width();
573 t.scale_factor = 1;
574 t.bdcnum = 0;
576 resultant_memo = 0;
577 resultant_inverse_memo = 0;
579 x[i2] = t.transform_scaled(t.scaled_inverse_transform(x[i2]) + diff_vector);
583 * Modify all projective parameters at once.
585 void gpt_set(point x[4]) {
587 resultant_memo = 0;
588 resultant_inverse_memo = 0;
590 _is_projective = 1;
592 for (int i = 0; i < 4; i++)
593 this->x[i] = x[i];
597 void gpt_set(point x1, point x2, point x3, point x4) {
598 point x[4] = {x1, x2, x3, x4};
599 gpt_set(x);
603 * Get the specified projective parameter
605 point gpt_get(int point) {
606 assert (_is_projective);
607 assert (point >= 0);
608 assert (point < 4);
610 return x[point];
614 * Get the specified projective parameter
616 ale_pos gpt_get(int point, int dim) {
617 assert (_is_projective);
618 assert (dim >= 0);
619 assert (dim < 2);
621 return gpt_get(point)[dim];
625 * Translate by a given amount
627 void translate(point p) {
629 resultant_memo = 0;
630 resultant_inverse_memo = 0;
632 if (_is_projective)
633 for (int i = 0; i < 4; i++)
634 x[i]+=p;
635 else {
636 eu[0] += p[0] / scale_factor;
637 eu[1] += p[1] / scale_factor;
642 * Rotate by a given amount about a given point.
644 void rotate(point p, ale_pos degrees) {
646 ale_pos radians = degrees * M_PI / (double) 180;
648 if (_is_projective)
649 for (int i = 0; i <= 4; i++) {
650 x[i] -= p;
651 x[i] = point(
652 x[i][0] * cos(radians) + x[i][1] * sin(radians),
653 x[i][1] * cos(radians) - x[i][0] * sin(radians));
654 x[i] += p;
655 } else {
656 point usi = unscaled_inverse_transform(p);
658 eu[2] += degrees;
659 resultant_memo = 0;
661 point p_rot = transform_unscaled(usi);
663 eu[0] -= (p_rot[0] - p[0]) / scale_factor;
664 eu[1] -= (p_rot[1] - p[1]) / scale_factor;
667 resultant_memo = 0;
668 resultant_inverse_memo = 0;
672 * Set the specified barrel distortion parameter.
674 void bd_set(unsigned int degree, ale_pos value) {
675 assert (degree < bdcnum);
676 bdc[degree] = value;
680 * Set all barrel distortion parameters.
682 void bd_set(unsigned int degree, ale_pos values[BARREL_DEGREE]) {
683 assert (degree <= BARREL_DEGREE);
684 bdcnum = degree;
685 for (unsigned int d = 0; d < degree; d++)
686 bdc[d] = values[d];
690 * Get the all barrel distortion parameters.
692 void bd_get(ale_pos result[BARREL_DEGREE]) {
693 for (unsigned int d = 0; d < bdcnum; d++)
694 result[d] = bdc[d];
698 * Get the specified barrel distortion parameter.
700 ale_pos bd_get(unsigned int degree) {
701 assert (degree < bdcnum);
702 return bdc[degree];
706 * Get the number of barrel distortion parameters.
708 unsigned int bd_count() {
709 return bdcnum;
713 * Get the maximum allowable number of barrel distortion parameters.
715 unsigned int bd_max() {
716 return BARREL_DEGREE;
720 * Modify the specified barrel distortion parameter.
722 void bd_modify(unsigned int degree, ale_pos diff) {
723 assert (degree < bdcnum);
724 bd_set(degree, bd_get(degree) + diff);
728 * Rescale a transform with a given factor.
730 void rescale(ale_pos factor) {
732 resultant_memo = 0;
733 resultant_inverse_memo = 0;
735 if (_is_projective) {
737 for (int i = 0; i < 4; i++)
738 for (int j = 0; j < 2; j++)
739 x[i][j] *= factor;
741 } else {
742 #if 0
744 * Euclidean scaling is handled in resultant().
746 for (int i = 0; i < 2; i++)
747 eu[i] *= factor;
748 #endif
751 scale_factor *= factor;
755 * Set a new domain.
758 void set_domain(unsigned int new_height, unsigned int new_width) {
759 resultant_memo = 0;
760 resultant_inverse_memo = 0;
762 input_width = new_width;
763 input_height = new_height;
767 * Set the dimensions of the image.
769 void set_dimensions(const image *im) {
771 int new_height = (int) im->height();
772 int new_width = (int) im->width();
774 resultant_memo = 0;
775 resultant_inverse_memo = 0;
777 if (_is_projective) {
780 * If P(w, x, y, z) is a projective transform mapping
781 * the corners of the unit square to points w, x, y, z,
782 * and Q(w, x, y, z)(i, j) == P(w, x, y, z)(ai, bj),
783 * then we have:
785 * Q(w, x, y, z) == P( P(w, x, y, z)(0, 0),
786 * P(w, x, y, z)(a, 0),
787 * P(w, x, y, z)(a, b),
788 * P(w, x, y, z)(0, b) )
790 * If we note that P(w, x, y, z)(0, 0) == w, we can
791 * omit a calculation.
793 * We take 'a' as the ratio (new_height /
794 * old_height) and 'b' as the ratio (new_width /
795 * old_width) if we want the common upper left-hand
796 * region of both new and old images to map to the same
797 * area.
799 * Since we're not mapping from the unit square, we
800 * take 'a' as new_height and 'b' as new_width to
801 * accommodate the existing scale factor.
804 point _x, _y, _z;
806 _x = transform_unscaled(point(new_height, 0 ));
807 _y = transform_unscaled(point(new_height, new_width));
808 _z = transform_unscaled(point( 0 , new_width));
810 x[1] = _x;
811 x[2] = _y;
812 x[3] = _z;
816 input_height = new_height;
817 input_width = new_width;
821 * Get the position and dimensions of a pixel P mapped from one
822 * coordinate system to another, using the forward transformation.
823 * This function uses scaled input coordinates.
825 void map_area(point p, point *q, ale_pos d[2]) {
828 * Determine the coordinates in the target frame for the source
829 * image pixel P and two adjacent source pixels.
832 (*q) = transform_scaled(p);
833 point q0 = transform_scaled(point(p[0] + 1, p[1]));
834 point q1 = transform_scaled(point(p[0], p[1] + 1));
837 * Calculate the distance between source image pixel and
838 * adjacent source pixels, measured in the coordinate system of
839 * the target frame.
842 ale_pos ui = fabs(q0[0] - (*q)[0]);
843 ale_pos uj = fabs(q0[1] - (*q)[1]);
844 ale_pos vi = fabs(q1[0] - (*q)[0]);
845 ale_pos vj = fabs(q1[1] - (*q)[1]);
848 * We map the area of the source image pixel P onto the target
849 * frame as a rectangular area oriented on the target frame's
850 * axes. Note that this results in an area that may be the
851 * wrong shape or orientation.
853 * We define two estimates of the rectangle's dimensions below.
854 * For rotations of 0, 90, 180, or 270 degrees, max and sum are
855 * identical. For other orientations, sum is too large and max
856 * is too small. We use the mean of max and sum, which we then
857 * divide by two to obtain the distance between the center and
858 * the edge.
861 ale_pos maxi = (ui > vi) ? ui : vi;
862 ale_pos maxj = (uj > vj) ? uj : vj;
863 ale_pos sumi = ui + vi;
864 ale_pos sumj = uj + vj;
866 d[0] = (maxi + sumi) / 4;
867 d[1] = (maxj + sumj) / 4;
871 * Get the position and dimensions of a pixel P mapped from one
872 * coordinate system to another, using the forward transformation.
873 * This function uses unscaled input coordinates.
875 void map_area_unscaled(point p, point *q, ale_pos d[2]) {
878 * Determine the coordinates in the target frame for the source
879 * image pixel P and two adjacent source pixels.
882 (*q) = transform_unscaled(p);
883 point q0 = transform_unscaled(point(p[0] + 1, p[1]));
884 point q1 = transform_unscaled(point(p[0], p[1] + 1));
887 * Calculate the distance between source image pixel and
888 * adjacent source pixels, measured in the coordinate system of
889 * the target frame.
892 ale_pos ui = fabs(q0[0] - (*q)[0]);
893 ale_pos uj = fabs(q0[1] - (*q)[1]);
894 ale_pos vi = fabs(q1[0] - (*q)[0]);
895 ale_pos vj = fabs(q1[1] - (*q)[1]);
898 * We map the area of the source image pixel P onto the target
899 * frame as a rectangular area oriented on the target frame's
900 * axes. Note that this results in an area that may be the
901 * wrong shape or orientation.
903 * We define two estimates of the rectangle's dimensions below.
904 * For rotations of 0, 90, 180, or 270 degrees, max and sum are
905 * identical. For other orientations, sum is too large and max
906 * is too small. We use the mean of max and sum, which we then
907 * divide by two to obtain the distance between the center and
908 * the edge.
911 ale_pos maxi = (ui > vi) ? ui : vi;
912 ale_pos maxj = (uj > vj) ? uj : vj;
913 ale_pos sumi = ui + vi;
914 ale_pos sumj = uj + vj;
916 d[0] = (maxi + sumi) / 4;
917 d[1] = (maxj + sumj) / 4;
921 * Get the position and dimensions of a pixel P mapped from one
922 * coordinate system to another, using the inverse transformation. If
923 * SCALE_FACTOR is not equal to one, divide out the scale factor to
924 * obtain unscaled coordinates. This method is very similar to the
925 * map_area method above.
927 void unscaled_map_area_inverse(point p, point *q, ale_pos d[2]) {
930 * Determine the coordinates in the target frame for the source
931 * image pixel P and two adjacent source pixels.
934 (*q) = scaled_inverse_transform(p);
935 point q0 = scaled_inverse_transform(point(p[0] + 1, p[1]));
936 point q1 = scaled_inverse_transform(point(p[0], p[1] + 1));
940 * Calculate the distance between source image pixel and
941 * adjacent source pixels, measured in the coordinate system of
942 * the target frame.
945 ale_pos ui = fabs(q0[0] - (*q)[0]);
946 ale_pos uj = fabs(q0[1] - (*q)[1]);
947 ale_pos vi = fabs(q1[0] - (*q)[0]);
948 ale_pos vj = fabs(q1[1] - (*q)[1]);
951 * We map the area of the source image pixel P onto the target
952 * frame as a rectangular area oriented on the target frame's
953 * axes. Note that this results in an area that may be the
954 * wrong shape or orientation.
956 * We define two estimates of the rectangle's dimensions below.
957 * For rotations of 0, 90, 180, or 270 degrees, max and sum are
958 * identical. For other orientations, sum is too large and max
959 * is too small. We use the mean of max and sum, which we then
960 * divide by two to obtain the distance between the center and
961 * the edge.
964 ale_pos maxi = (ui > vi) ? ui : vi;
965 ale_pos maxj = (uj > vj) ? uj : vj;
966 ale_pos sumi = ui + vi;
967 ale_pos sumj = uj + vj;
969 d[0] = (maxi + sumi) / 4;
970 d[1] = (maxj + sumj) / 4;
972 if (scale_factor != 1) {
973 d[0] /= scale_factor;
974 d[1] /= scale_factor;
975 (*q)[0] /= scale_factor;
976 (*q)[1] /= scale_factor;
981 * Modify all projective parameters at once. Accommodate bugs in the
982 * version 0 transformation file handler (ALE versions 0.4.0p1 and
983 * earlier). This code is only called when using a transformation data
984 * file created with an old version of ALE.
986 void gpt_v0_set(point x[4]) {
988 _is_projective = 1;
991 * This is slightly modified code from version
992 * 0.4.0p1.
995 ale_pos delta_01 = x[1][0] - x[2][0];
996 ale_pos delta_02 = x[3][0] - x[2][0];
997 ale_pos sigma_0 = x[0][0] - x[1][0] + x[2][0] - x[3][0];
998 ale_pos delta_11 = x[1][1] - x[2][1];
999 ale_pos delta_12 = x[3][1] - x[2][1];
1000 ale_pos sigma_1 = x[0][1] - x[1][1] + x[2][1] - x[3][1];
1002 g = (sigma_0 * delta_12 - sigma_1 * delta_02)
1003 / (delta_01 * delta_12 - delta_11 * delta_02)
1004 / (input_width * scale_factor);
1006 h = (delta_01 * sigma_1 - delta_11 * sigma_0 )
1007 / (delta_01 * delta_12 - delta_11 * delta_02)
1008 / (input_height * scale_factor);
1010 a = (x[1][0] - x[0][0] + g * x[1][0])
1011 / (input_width * scale_factor);
1012 b = (x[3][0] - x[0][0] + h * x[3][0])
1013 / (input_height * scale_factor);
1014 c = x[0][0];
1016 d = (x[1][1] - x[0][1] + g * x[1][1])
1017 / (input_width * scale_factor);
1018 e = (x[3][1] - x[0][1] + h * x[3][1])
1019 / (input_height * scale_factor);
1020 f = x[0][1];
1022 resultant_memo = 1;
1023 resultant_inverse_memo = 0;
1025 this->x[0] = scaled_inverse_transform( point( 0 , 0 ) );
1026 this->x[1] = scaled_inverse_transform( point( (input_height * scale_factor), 0 ) );
1027 this->x[2] = scaled_inverse_transform( point( (input_height * scale_factor), (input_width * scale_factor) ) );
1028 this->x[3] = scaled_inverse_transform( point( 0 , (input_width * scale_factor) ) );
1030 resultant_memo = 0;
1031 resultant_inverse_memo = 0;
1035 * Modify all euclidean parameters at once. Accommodate bugs in the
1036 * version 0 transformation file handler (ALE versions 0.4.0p1 and
1037 * earlier). This code is only called when using a transformation data
1038 * file created with an old version of ALE.
1040 void eu_v0_set(ale_pos eu[3]) {
1043 * This is slightly modified code from version
1044 * 0.4.0p1.
1047 int i;
1049 x[0][0] = 0; x[0][1] = 0;
1050 x[1][0] = (input_width * scale_factor); x[1][1] = 0;
1051 x[2][0] = (input_width * scale_factor); x[2][1] = (input_height * scale_factor);
1052 x[3][0] = 0; x[3][1] = (input_height * scale_factor);
1055 * Rotate
1058 ale_pos theta = eu[2] * M_PI / 180;
1060 for (i = 0; i < 4; i++) {
1061 ale_pos _x[2];
1063 _x[0] = (x[i][0] - (input_width * scale_factor)/2) * cos(theta)
1064 + (x[i][1] - (input_height * scale_factor)/2) * sin(theta)
1065 + (input_width * scale_factor)/2;
1066 _x[1] = (x[i][1] - (input_height * scale_factor)/2) * cos(theta)
1067 - (x[i][0] - (input_width * scale_factor)/2) * sin(theta)
1068 + (input_height * scale_factor)/2;
1070 x[i][0] = _x[0];
1071 x[i][1] = _x[1];
1075 * Translate
1078 for (i = 0; i < 4; i++) {
1079 x[i][0] += eu[0];
1080 x[i][1] += eu[1];
1083 if (_is_projective) {
1084 gpt_v0_set(x);
1085 return;
1089 * Reconstruct euclidean parameters
1092 gpt_v0_set(x);
1094 point center((input_height * scale_factor) / 2, (input_width * scale_factor) / 2);
1095 point center_image = transform_scaled(center);
1097 this->eu[0] = (center_image[0] - center[0]) / scale_factor;
1098 this->eu[1] = (center_image[1] - center[1]) / scale_factor;
1100 point center_left((input_height * scale_factor) / 2, 0);
1101 point center_left_image = transform_scaled(center_left);
1103 ale_pos displacement = center_image[0] - center_left_image[0];
1105 this->eu[2] = asin(2 * displacement / (input_width * scale_factor)) / M_PI * 180;
1107 if (center_left_image[1] > center_image[1])
1108 this->eu[2] = this->eu[2] + 180;
1110 resultant_memo = 0;
1111 resultant_inverse_memo = 0;
1113 _is_projective = 0;
1117 void debug_output() {
1118 fprintf(stderr, "[t.do ih=%u, iw=%d x=[[%f %f] [%f %f] [%f %f] [%f %f]] eu=[%f %f %f]\n"
1119 " a-f=[%f %f %f %f %f %f %f %f] _a-_f=[%f %f %f %f %f %f %f %f]\n"
1120 " bdcnm=%d ip=%d rm=%d rim=%d sf=%f]\n",
1121 input_height, input_width,
1122 x[0][0], x[0][1],
1123 x[1][0], x[1][1],
1124 x[2][0], x[2][1],
1125 x[3][0], x[3][1],
1126 eu[0], eu[1], eu[2],
1127 a, b, c, d, e, f, g, h,
1128 _a, _b, _c, _d, _e, _f, _g, _h,
1129 bdcnum,
1130 _is_projective,
1131 resultant_memo,
1132 resultant_inverse_memo,
1133 scale_factor);
1138 #endif