// Copyright 2002, 2004, 2007 David Hilvert , // /* This file is part of the Anti-Lamenessing Engine. The Anti-Lamenessing Engine is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. The Anti-Lamenessing Engine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the Anti-Lamenessing Engine; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * trans_abstract.h: Abstract transformation superclass. */ #ifndef __trans_abstract_h__ #define __trans_abstract_h__ #include "image.h" #include "point.h" #include "pixel.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* * Number of coefficients used in correcting barrel distortion. */ #define BARREL_DEGREE 5 /* * Acceptable error for inverse barrel distortion, measured in scaled output * pixels. */ #define BARREL_INV_ERROR 0.01 struct trans_abstract { private: ale_pos bdc[BARREL_DEGREE]; // barrel-dist. coeffs. unsigned int bdcnum; // number of bdcs protected: ale_pos scale_factor; unsigned int input_height, input_width; virtual void specific_rescale(ale_pos factor) = 0; virtual void reset_memos() = 0; virtual void specific_set_dimensions(const image *im) = 0; public: trans_abstract() { bdcnum = 0; } trans_abstract &operator=(const trans_abstract &ta) { scale_factor = ta.scale_factor; input_width = ta.input_width; input_height = ta.input_height; bdcnum = ta.bdcnum; assert (bdcnum < BARREL_DEGREE); for (unsigned int d = 0; d < bdcnum; d++) bdc[d] = ta.bdc[d]; return *this; } trans_abstract (const trans_abstract &ta) { operator=(ta); } /* * Returns non-zero if the transformation might be non-Euclidean. */ virtual int is_projective() const = 0; /* * Get scale factor. */ ale_pos scale() const { return scale_factor; } /* * Get width of input image. */ ale_pos scaled_width() const { return (input_width * scale_factor); } /* * Get unscaled width of input image. */ unsigned int unscaled_width() const { return (unsigned int) input_width; } /* * Get height of input image; */ ale_pos scaled_height() const { return (input_height * scale_factor); } /* * Get unscaled height of input image. */ unsigned int unscaled_height() const { return (unsigned int) input_height; } /* * Barrel distortion radial component. */ ale_pos bdr(ale_pos r) const { assert (bdcnum < BARREL_DEGREE); ale_pos s = r; for (unsigned int d = 0; d < bdcnum; d++) s += bdc[d] * (pow(r, d + 2) - r); return s; } /* * Derivative of the barrel distortion radial component. */ ale_pos bdrd(ale_pos r) const { assert (bdcnum < BARREL_DEGREE); ale_pos s = 1; for (unsigned int d = 0; d < bdcnum; d++) s += bdc[d] * (pow(r, d + 1) - 1); return s; } /* * Barrel distortion. */ struct point bd(struct point p) const { if (bdcnum > 0) { point half_diag = point(unscaled_height(), unscaled_width()) / 2; p -= half_diag; ale_pos r = p.norm() / half_diag.norm(); if (r > 0.00001) p *= bdr(r)/r; p += half_diag; } return p; } /* * Barrel distortion inverse. */ struct point bdi(struct point p) const { if (bdcnum > 0) { point half_diag = point(unscaled_height(), unscaled_width()) / 2; p -= half_diag; ale_pos r = p.norm() / half_diag.norm(); ale_pos s = r; while (fabs(r - bdr(s)) * half_diag.norm() > BARREL_INV_ERROR) s += (r - bdr(s)) / bdrd(s); if (r > 0.0001) p *= s / r; p += half_diag; } assert (!isnan(p[0]) && !isnan(p[1])); return p; } /* * Transformation sans barrel distortion */ virtual struct point pe(struct point p) const = 0; /* * Transformation inverse sans barrel distortion */ virtual struct point pei(struct point p) const = 0; /* * Map unscaled point p. */ struct point transform_unscaled(struct point p) const { return pe(bdi(p)); } /* * Transform point p. * * Barrel distortion correction followed by a projective/euclidean * transformation. */ struct point transform_scaled(struct point p) const { return transform_unscaled(p / scale_factor); } #if 0 /* * operator() is the transformation operator. */ struct point operator()(struct point p) { return transform(p); } #endif /* * Map point p using the inverse of the transform into * the unscaled image space. */ struct point unscaled_inverse_transform(struct point p) const { return bd(pei(p)); } /* * Map point p using the inverse of the transform. * * Projective/euclidean inverse followed by barrel distortion. */ struct point scaled_inverse_transform(struct point p) const { assert (p.defined()); point q = unscaled_inverse_transform(p); q[0] *= scale_factor; q[1] *= scale_factor; return q; } /* * Calculate projective transformation parameters from a euclidean * transformation. */ virtual void eu_to_gpt() = 0; /* * Set the tonal multiplier. */ virtual void set_tonal_multiplier(pixel p) = 0; /* * Get the tonal multiplier. */ virtual pixel get_tonal_multiplier(struct point p) const = 0; virtual pixel get_inverse_tonal_multiplier(struct point p) const = 0; /* * Modify a euclidean transform in the indicated manner. */ virtual void eu_modify(int i1, ale_pos diff) = 0; /* * Rotate about a given point in the original reference frame. */ virtual void eu_rotate_about_scaled(point center, ale_pos diff) = 0; /* * Modify all euclidean parameters at once. */ virtual void eu_set(ale_pos eu[3]) = 0; /* * Get the specified euclidean parameter */ virtual ale_pos eu_get(int param) const = 0; /* * Modify a projective transform in the indicated manner. */ virtual void gpt_modify(int i1, int i2, ale_pos diff) = 0; /* * Modify a projective transform according to the group operation. */ virtual void gr_modify(int i1, int i2, ale_pos diff) = 0; /* * Modify all projective parameters at once. */ virtual void gpt_set(point x[4]) = 0; virtual void gpt_set(point x1, point x2, point x3, point x4) = 0; /* * Snap positional parameters to the specified resolution. */ virtual void snap(ale_pos interval) = 0; /* * Get the specified projective parameter */ virtual point gpt_get(int point) const = 0; /* * Get the specified projective parameter */ virtual ale_pos gpt_get(int point, int dim) = 0; /* * Check equality of transformation parameters. */ virtual int operator==(const trans_abstract &t) const { /* * Small tolerances (< 10^-6?) can cause odd errors, * possibly due to float<->double conversion issues. */ double zero_tolerance = 0.01; if (scale() != t.scale()) return 0; if (is_projective() != t.is_projective()) return 0; if (is_projective()) { assert (t.is_projective()); for (int i = 0; i < 4; i++) for (int d = 0; d < 2; d++) { double abs_difference = fabs(gpt_get(i)[d] - t.gpt_get(i)[d]); if (abs_difference > zero_tolerance) return 0; } } else { assert (!t.is_projective()); for (int i = 0; i < 3; i++) { double abs_difference = fabs(eu_get(i) - t.eu_get(i)); if (abs_difference > zero_tolerance) return 0; } } return 1; } virtual int operator!=(const trans_abstract &t) const { return !(operator==(t)); } /* * Translate by a given amount */ virtual void translate(point p) = 0; /* * Rotate by a given amount about a given point. */ virtual void rotate(point p, ale_pos degrees) = 0; /* * Set the specified barrel distortion parameter. */ void bd_set(unsigned int degree, ale_pos value) { assert (degree < bdcnum); bdc[degree] = value; } /* * Set all barrel distortion parameters. */ void bd_set(unsigned int degree, ale_pos values[BARREL_DEGREE]) { assert (degree <= BARREL_DEGREE); bdcnum = degree; for (unsigned int d = 0; d < degree; d++) bdc[d] = values[d]; } /* * Get all barrel distortion parameters. */ void bd_get(ale_pos result[BARREL_DEGREE]) { for (unsigned int d = 0; d < bdcnum; d++) result[d] = bdc[d]; } /* * Get the specified barrel distortion parameter. */ ale_pos bd_get(unsigned int degree) { assert (degree < bdcnum); return bdc[degree]; } /* * Get the number of barrel distortion parameters. */ unsigned int bd_count() { return bdcnum; } /* * Get the maximum allowable number of barrel distortion parameters. */ unsigned int bd_max() { return BARREL_DEGREE; } /* * Modify the specified barrel distortion parameter. */ void bd_modify(unsigned int degree, ale_pos diff) { assert (degree < bdcnum); bd_set(degree, bd_get(degree) + diff); } /* * Rescale a transform with a given factor. */ void rescale(ale_pos factor) { specific_rescale(factor); scale_factor *= factor; } /* * Set a new domain. */ void set_domain(unsigned int new_height, unsigned int new_width) { reset_memos(); input_width = new_width; input_height = new_height; } /* * Set the dimensions of the image. */ void set_dimensions(const image *im) { int new_height = (int) im->height(); int new_width = (int) im->width(); reset_memos(); specific_set_dimensions(im); input_height = new_height; input_width = new_width; } /* * Get the position and dimensions of a pixel P mapped from one * coordinate system to another, using the forward transformation. * This function uses scaled input coordinates. */ virtual void map_area(point p, point *q, ale_pos d[2]) { /* * Determine the coordinates in the target frame for the source * image pixel P and two adjacent source pixels. */ (*q) = transform_scaled(p); point q0 = transform_scaled(point(p[0] + 1, p[1])); point q1 = transform_scaled(point(p[0], p[1] + 1)); /* * Calculate the distance between source image pixel and * adjacent source pixels, measured in the coordinate system of * the target frame. */ ale_pos ui = fabs(q0[0] - (*q)[0]); ale_pos uj = fabs(q0[1] - (*q)[1]); ale_pos vi = fabs(q1[0] - (*q)[0]); ale_pos vj = fabs(q1[1] - (*q)[1]); /* * We map the area of the source image pixel P onto the target * frame as a rectangular area oriented on the target frame's * axes. Note that this results in an area that may be the * wrong shape or orientation. * * We define two estimates of the rectangle's dimensions below. * For rotations of 0, 90, 180, or 270 degrees, max and sum are * identical. For other orientations, sum is too large and max * is too small. We use the mean of max and sum, which we then * divide by two to obtain the distance between the center and * the edge. */ ale_pos maxi = (ui > vi) ? ui : vi; ale_pos maxj = (uj > vj) ? uj : vj; ale_pos sumi = ui + vi; ale_pos sumj = uj + vj; d[0] = (maxi + sumi) / 4; d[1] = (maxj + sumj) / 4; } /* * Get the position and dimensions of a pixel P mapped from one * coordinate system to another, using the forward transformation. * This function uses unscaled input coordinates. */ virtual void map_area_unscaled(point p, point *q, ale_pos d[2]) { /* * Determine the coordinates in the target frame for the source * image pixel P and two adjacent source pixels. */ (*q) = transform_unscaled(p); point q0 = transform_unscaled(point(p[0] + 1, p[1])); point q1 = transform_unscaled(point(p[0], p[1] + 1)); /* * Calculate the distance between source image pixel and * adjacent source pixels, measured in the coordinate system of * the target frame. */ ale_pos ui = fabs(q0[0] - (*q)[0]); ale_pos uj = fabs(q0[1] - (*q)[1]); ale_pos vi = fabs(q1[0] - (*q)[0]); ale_pos vj = fabs(q1[1] - (*q)[1]); /* * We map the area of the source image pixel P onto the target * frame as a rectangular area oriented on the target frame's * axes. Note that this results in an area that may be the * wrong shape or orientation. * * We define two estimates of the rectangle's dimensions below. * For rotations of 0, 90, 180, or 270 degrees, max and sum are * identical. For other orientations, sum is too large and max * is too small. We use the mean of max and sum, which we then * divide by two to obtain the distance between the center and * the edge. */ ale_pos maxi = (ui > vi) ? ui : vi; ale_pos maxj = (uj > vj) ? uj : vj; ale_pos sumi = ui + vi; ale_pos sumj = uj + vj; d[0] = (maxi + sumi) / 4; d[1] = (maxj + sumj) / 4; } /* * Get the position and dimensions of a pixel P mapped from one * coordinate system to another, using the inverse transformation. If * SCALE_FACTOR is not equal to one, divide out the scale factor to * obtain unscaled coordinates. This method is very similar to the * map_area method above. */ virtual void unscaled_map_area_inverse(point p, point *q, ale_pos d[2]) { /* * Determine the coordinates in the target frame for the source * image pixel P and two adjacent source pixels. */ (*q) = scaled_inverse_transform(p); point q0 = scaled_inverse_transform(point(p[0] + 1, p[1])); point q1 = scaled_inverse_transform(point(p[0], p[1] + 1)); /* * Calculate the distance between source image pixel and * adjacent source pixels, measured in the coordinate system of * the target frame. */ ale_pos ui = fabs(q0[0] - (*q)[0]); ale_pos uj = fabs(q0[1] - (*q)[1]); ale_pos vi = fabs(q1[0] - (*q)[0]); ale_pos vj = fabs(q1[1] - (*q)[1]); /* * We map the area of the source image pixel P onto the target * frame as a rectangular area oriented on the target frame's * axes. Note that this results in an area that may be the * wrong shape or orientation. * * We define two estimates of the rectangle's dimensions below. * For rotations of 0, 90, 180, or 270 degrees, max and sum are * identical. For other orientations, sum is too large and max * is too small. We use the mean of max and sum, which we then * divide by two to obtain the distance between the center and * the edge. */ ale_pos maxi = (ui > vi) ? ui : vi; ale_pos maxj = (uj > vj) ? uj : vj; ale_pos sumi = ui + vi; ale_pos sumj = uj + vj; d[0] = (maxi + sumi) / 4; d[1] = (maxj + sumj) / 4; if (scale_factor != 1) { d[0] /= scale_factor; d[1] /= scale_factor; (*q)[0] /= scale_factor; (*q)[1] /= scale_factor; } } /* * Modify all projective parameters at once. Accommodate bugs in the * version 0 transformation file handler (ALE versions 0.4.0p1 and * earlier). This code is only called when using a transformation data * file created with an old version of ALE. */ virtual void gpt_v0_set(point x[4]) = 0; /* * Modify all euclidean parameters at once. Accommodate bugs in the * version 0 transformation file handler (ALE versions 0.4.0p1 and * earlier). This code is only called when using a transformation data * file created with an old version of ALE. */ virtual void eu_v0_set(ale_pos eu[3]) = 0; virtual void debug_output() = 0; virtual ~trans_abstract() { } }; #endif