initial commit

This commit is contained in:
Mauro Torrez
2022-07-30 14:46:04 -03:00
commit 47650131de
312 changed files with 212465 additions and 0 deletions

32
d3/align.cc Normal file
View File

@@ -0,0 +1,32 @@
// Copyright 2003 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
#include "align.h"
/*
* See align.h for details on these variables.
*/
ale_pos align::_init_angle;
et *align::alignment_array = NULL;
int align::_vp_adjust = 1;
int align::_vo_adjust = 1;
tload_t *align::tload = NULL;
tsave_t *align::tsave = NULL;

643
d3/align.h Normal file
View File

@@ -0,0 +1,643 @@
// Copyright 2003 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
/*
* d3/align.h: Handle alignment of view pyramids.
*
* XXX: this class assumes that the view volume is a right rectangular-based
* pyramid, which holds for most photographic situations.
*
* XXX: this class assumes that the horizontal and vertical angles of the view
* volume pyramid are proportional to the horizontal and vertical pixel
* resolutions.
*/
#ifndef __d3align_h__
#define __d3align_h__
#include "point.h"
#include "et.h"
#include "pt.h"
#include "tfile.h"
class align {
static ale_pos _init_angle;
static et *alignment_array;
static int _vp_adjust;
static int _vo_adjust;
static tload_t *tload;
static tsave_t *tsave;
/*
* Estimate the point at which the pyramidal axis passes through the
* plane, based on the given 2-dimensional transformation and the given
* point set indicating the corners of the quadrilateral of
* intersection. (Both transformation and point set alone are
* sufficient for calculation.)
*
* Using the point set, the desired point is exactly the point of
* intersection between line segments drawn between opposite corners of
* the quadrilateral of intersection. The algebraic details of this
* calculation are desribed below.
*
* If we represent the desired point by (e1, e2), and the four corners
* of the quadrilateral of intersection by (a1, a2), ..., (d1, d2) in
* clockwise (or counter-clockwise) order, then we get the two-equation
* vector system (implicitly four equations)
*
* (e1, e2) = (a1, a2) + x[(c1, c2) - (a1, a2)]
* = (b1, b2) + y[(d1, d2) - (b1, b2)]
*
* Solving for x in terms of y, we get the two-equation system
*
* x = (b1 - yb1 + yd1 - a1) / (c1 - a1)
* = (b2 - yb2 + yd2 - a2) / (c2 - a2)
*
* And
*
* y = (c1b2 - c1a2 - a1b2 - c2b1 + c2a1 - a2b1)
* /(-c2b1 + c2d1 + a2b1 - a2d1 + c1b2 - c1d2 - a1b2 + a1d2)
*
* However, it's much easier just to project the center point of the
* source image onto the quadrilateral of intersection using the
* given 2-dimensional transformation.
*/
static d2::point axis_intersection(d2::point a, d2::point b, d2::point c, d2::point d, d2::transformation t) {
#if 0
ale_pos y = (c[0]*b[1] - c[0]*a[1] - a[0]*b[1] - c[1]*b[0]
+ c[1]*a[0] - a[1]*b[0])
/(-c[1]*b[0] + c[1]*d[0] + a[1]*b[0] - a[1]*d[0]
+ c[0]*b[1] - c[0]*d[1] - a[0]*b[1] + a[0]*d[1]);
return b + y * (d - b);
#else
return t.transform_scaled(d2::point((t.scaled_height() - 1) / 2, (t.scaled_width() - 1) / 2));
#endif
}
/*
* gd_position is a gradient-descent automatic positioning algorithm
* used to determine the location of the view pyramid apex.
*
* Starting from an initial camera position ESTIMATE, this method
* determines the angles between each pair of legs in the view volume
* (there are six pairs). The sum of squares of the differences
* between the thusly determined angles and the known desired angles is
* then used as an error metric, and a kind of gradient descent is used
* to find a position for which this metric is minimized.
*/
static point gd_position(point estimate, d2::point a, d2::point b, d2::point c, d2::point d, d2::transformation t) {
ale_pos w = t.scaled_width();
ale_pos h = t.scaled_height();
/*
* The desired diagonal angle is given (as init_angle).
* Calculate the desired side angles as follows:
*
* The distance to the apex is
*
* D = sqrt(h*h + w*w) / (2 * tan(init_angle/2))
*
* Then
*
* desired_h_angle = 2 * arctan(h / (2 * sqrt(D*D + w*w/4)))
* desired_w_angle = 2 * arctan(w / (2 * sqrt(D*D + h*h/4)))
*/
ale_pos D = (ale_pos) sqrt(h * h + w * w)
/ (ale_pos) (2 * tan(_init_angle/2));
ale_pos desired_h_angle = (double) (2 * atan((double) ((double) h / (2 * sqrt((double) (D*D) + (double) (w*w)/4)))));
ale_pos desired_w_angle = (double) (2 * atan((double) ((double) w / (2 * sqrt((double) (D*D) + (double) (h*h)/4)))));
ale_pos estimate_h1_angle = estimate.anglebetw(a, b);
ale_pos estimate_h2_angle = estimate.anglebetw(c, d);
ale_pos estimate_w1_angle = estimate.anglebetw(a, d);
ale_pos estimate_w2_angle = estimate.anglebetw(b, c);
ale_pos estimate_d1_angle = estimate.anglebetw(a, c);
ale_pos estimate_d2_angle = estimate.anglebetw(b, d);
ale_pos error = sqrt(pow(estimate_h1_angle - desired_h_angle, 2)
+ pow(estimate_h2_angle - desired_h_angle, 2)
+ pow(estimate_w1_angle - desired_w_angle, 2)
+ pow(estimate_w2_angle - desired_w_angle, 2)
+ pow(estimate_d1_angle - _init_angle , 2)
+ pow(estimate_d2_angle - _init_angle , 2));
/*
* Vary the magnitude by which each coordinate of the position
* can be changed at each step.
*/
ale_pos view_angle = _init_angle;
for (ale_pos magnitude = estimate[2] / 2;
magnitude >= 1;
magnitude /= 2) {
/*
* Continue searching at this magnitude while error <
* old_error. (Initialize old_error accordingly.)
*/
ale_pos old_error = error * 2;
while(old_error > error) {
// ale_pos D = sqrt(h * h + w * w)
// / (2 * tan(view_angle/2));
// ale_pos desired_h_angle = 2 * atan(h / (2 * sqrt(D*D + w*w/4)));
// ale_pos desired_w_angle = 2 * atan(w / (2 * sqrt(D*D + h*h/4)));
// fprintf(stderr, ".");
// fprintf(stderr, "estimate: [%f %f %f %f %f %f]\n",
// estimate_h1_angle,
// estimate_h2_angle,
// estimate_w1_angle,
// estimate_w2_angle,
// estimate_d1_angle,
// estimate_d2_angle);
//
// fprintf(stderr, "desired : [%f %f %f]\n",
// desired_h_angle,
// desired_w_angle,
// view_angle);
old_error = error;
for (ale_pos c0 = -magnitude; c0 <= magnitude; c0 += magnitude)
for (ale_pos c1 = -magnitude; c1 <= magnitude; c1 += magnitude)
for (ale_pos c2 = -magnitude; c2 <= magnitude; c2 += magnitude)
for (ale_pos c3 = -magnitude; c3 <= magnitude; c3 += magnitude) {
if (c3 > 10)
c3 = 10;
// fprintf(stderr, "[%f %f %f]\n", c0, c1, c2);
estimate[0] += c0;
estimate[1] += c1;
estimate[2] += c2;
// view_angle += c3 / 30;
ale_pos D = (ale_pos) sqrt(h * h + w * w)
/ (ale_pos) (2 * tan(view_angle/2));
ale_pos desired_h_angle = 2 * atan((double) h / (2 * sqrt((double) (D*D) + (double) (w*w)/4)));
ale_pos desired_w_angle = 2 * atan((double) w / (2 * sqrt((double) D*D + (double) h*h/4)));
estimate_h1_angle = estimate.anglebetw(a, b);
estimate_h2_angle = estimate.anglebetw(c, d);
estimate_w1_angle = estimate.anglebetw(a, d);
estimate_w2_angle = estimate.anglebetw(b, c);
estimate_d1_angle = estimate.anglebetw(a, c);
estimate_d2_angle = estimate.anglebetw(b, d);
ale_pos perturbed_error =
sqrt(pow(estimate_h1_angle - desired_h_angle, 2)
+ pow(estimate_h2_angle - desired_h_angle, 2)
+ pow(estimate_w1_angle - desired_w_angle, 2)
+ pow(estimate_w2_angle - desired_w_angle, 2)
+ pow(estimate_d1_angle - view_angle , 2)
+ pow(estimate_d2_angle - view_angle , 2));
if (perturbed_error < error) {
error = perturbed_error;
} else {
estimate[0] -= c0;
estimate[1] -= c1;
estimate[2] -= c2;
// view_angle -= c3 / 30;
}
}
}
}
// fprintf(stderr, "error %f\n", error);
return estimate;
}
public:
/*
* Get alignment for frame N.
*/
static et of(unsigned int n) {
assert (n < d2::image_rw::count());
assert (alignment_array);
return alignment_array[n];
}
static double angle_of(unsigned int n) {
return _init_angle;
}
static pt projective(unsigned int n) {
return pt(d2::align::of(n), of(n), angle_of(n));
}
static void set_tload(tload_t *t) {
tload = t;
}
static void set_tsave(tsave_t *t) {
tsave = t;
}
static void write_alignments() {
int count = d2::image_rw::count();
if (count > 0)
tsave_first(tsave, projective(0));
for (int i = 1; i < count; i++) {
tsave_next(tsave, projective(i));
}
}
static void vp_adjust() {
_vp_adjust = 1;
}
static void vp_noadjust() {
_vp_adjust = 0;
}
static void vo_adjust() {
_vo_adjust = 1;
}
static void vo_noadjust() {
_vo_adjust = 0;
}
/*
* Set the initial estimated diagonal viewing angle of the view
* pyramid. This is the angle formed, e.g., between the top-left and
* bottom-right legs of the view pyramid.
*/
static void init_angle(ale_pos a) {
_init_angle = a;
}
/*
* Initialize d3 transformations from d2 transformations.
*
* Here are three possible approaches for calculating camera position
* based on a known diagonal viewing angle and known area of
* intersection of the view volume with a fixed plane:
*
* (1) divide the rectangular-based pyramidal view volume into two
* tetrahedra. A coordinate system is selected so that the triangle of
* intersection between one of the tetrahedra and the fixed plane has
* coordinates (0, 0, 0), (1, 0, 0), and (a, b, 0), for some (a, b).
* The law of cosines is then used to derive three equations
* associating the three known angles at the apex with the lengths of
* the edges of the tetrahedron. The solution of this system of
* equations gives the coordinates of the apex in terms of a, b, and
* the known angles. These coordinates are then transformed back to
* the original coordinate system.
*
* (2) The gradient descent approach taken by the method gd_position().
*
* (3) Assume that the camera is aimed (roughly) perpendicular to the
* plane. In this case, the pyramidal axis forms with each adjacent
* pyramidal edge, in combination with a segment in the plane, a right
* triangle. The distance of the camera from the plane is d/(2 *
* tan(theta/2)), where d is the distance between opposite corners of
* the quadrilateral of intersection and theta is the angle between
* opposite pairs of edges of the view volume.
*
* As an initial approach to the problem, we use (3) followed by (2).
*
* After position is estimated, we determine orientation from position.
* In order to do this, we determine the point at which the axis of
* the view pyramid passes through the plane, as described in the
* method axis_intersection().
*/
static void init_from_d2() {
assert (alignment_array == NULL);
/*
* Initialize the alignment array.
*/
alignment_array = (et *) malloc (d2::image_rw::count() * sizeof(et));
assert (alignment_array);
if (!alignment_array) {
fprintf(stderr, "\n\n*** Unable to allocate memory for 3D alignment array. ***\n\n");
exit(1);
}
/*
* Iterate over input frames
*/
for (unsigned int i = 0; i < d2::image_rw::count(); i++) {
/*
* Obtain the four mapped corners of the quadrilateral
* of intersection.
*/
d2::transformation t = d2::align::of(i);
d2::point a = t.transform_scaled(d2::point(0, 0));
d2::point b = t.transform_scaled(d2::point(t.scaled_height() - 1, 0));
d2::point c = t.transform_scaled(d2::point(t.scaled_height() - 1, t.scaled_width() - 1));
d2::point d = t.transform_scaled(d2::point(0, t.scaled_width() - 1));
/*
* Estimate the point at which the pyramidal axis
* passes through the plane. We denote the point as
* 'e'.
*/
d2::point e = axis_intersection(a, b, c, d, t);
/*
* Determine the average distance between opposite
* corners, and calculate the distance to the camera
* based on method (3) described above.
*/
ale_pos dist1 = sqrt((a[0] - c[0])
* (a[0] - c[0])
+ (a[1] - c[1])
* (a[1] - c[1]));
ale_pos dist2 = sqrt((b[0] - d[0])
* (b[0] - d[0])
+ (b[1] - d[1])
* (b[1] - d[1]));
ale_pos avg_dist = (dist1 + dist2) / 2;
ale_pos tangent = tan(_init_angle / 2);
ale_pos distance = avg_dist / (2 * tangent);
/*
* Set the position of the camera based on the estimate
* from method (3). This is used as the initial
* position for method (2). We assume that the camera
* is placed so that its 0 and 1 coordinates match those
* of e.
*/
point estimate;
estimate[0] = e[0];
estimate[1] = e[1];
estimate[2] = distance;
// fprintf(stderr, "position (n=%d) %f %f %f\n", i, estimate[0], estimate[1], estimate[2]);
/*
* Perform method (2).
*/
estimate = gd_position(estimate, a, b, c, d, t);
// fprintf(stderr, ".");
/*
* Assign transformation values based on the output of
* method (2), by modifying transformation parameters
* from the identity transformation.
*/
alignment_array[i] = et::identity();
// fprintf(stderr, "position (n=%d) %f %f %f\n", i, estimate[0], estimate[1], estimate[2]);
/*
* Modify translation values, if desired.
*/
if (_vp_adjust) {
alignment_array[i].modify_translation(0, -estimate[0]);
alignment_array[i].modify_translation(1, -estimate[1]);
alignment_array[i].modify_translation(2, -estimate[2]);
} else {
/*
* Establish a default translation value.
*/
alignment_array[i].modify_translation(2, -1);
}
/*
* Load any transformations.
*/
if (i == 0) {
int is_default;
pt p = tload_first(tload, projective(i), &is_default);
alignment_array[i] = p.e();
_init_angle = p.view_angle();
} else {
int is_default;
pt p = tload_next(tload, projective(i), &is_default);
alignment_array[i] = p.e();
if (p.view_angle() != _init_angle) {
fprintf(stderr, "Error: variation in view angle is not currently supported.\n");
exit(1);
}
}
/*
* Assert that the z-axis translation is nonzero. This
* is important for determining rotation values.
*/
assert (estimate[2] != 0);
/*
* Check whether rotation adjustment must proceed.
*/
if (!_vo_adjust) {
continue;
}
/*
* Modify rotation values
*
* The axis of the view pyramid should be mapped onto
* the z-axis. Hence, the point of intersection
* between the world-coordinates xy-plane and the axis
* of the pyramid should occur at (0, 0) in local x-y
* coordinates.
*
* If we temporarily assume no rotation about the
* z-axis (e2 == 0), then the Euler angles (e0, e1, e2)
* used to determine rotation give us:
*
* x' = x cos e1 - (z cos e0 - y sin e0) sin e1
* y' = y cos e0 + z sin e0
* z' = (z cos e0 - y sin e0) cos e1 + x sin e1
*
* Setting x' = y' = 0, we get:
*
* e0 = arctan (-y/z)
* e1 = arctan (x/(z cos e0 - y sin e0))
* [optionally add pi to achieve z' < 0]
*
* To set e2, taking T to be the 3D transformation as a
* function of e2, we first ensure that
*
* tan(T(e2, a)[1] / T(e2, a)[0])
* ==
* tan(width / height)
*
* by assigning
*
* e2 += tan(width / height)
* - tan(T(e2, a)[1] / T(e2, a)[0])
*
* Once this condition is satisfied, we conditionally
* assign
*
* e2 += pi
*
* to ensure that
*
* T(e2, a)[0] < 0
* T(e2, a)[1] < 0
*
* which places the transformed point in the lower-left
* quadrant. This is correct, since point A is mapped
* from (0, 0), the most negative point in the image.
*/
point e_translated = alignment_array[i](e);
// fprintf(stderr, "axis-intersection (n=%d) e1 %f e0 %f e_t1 %f e_t0 %f e_t2 %f\n",
// i,
// e[1],
// e[0],
// alignment_array[i](e)[1],
// alignment_array[i](e)[0],
// alignment_array[i](e)[2]);
//
// fprintf(stderr, "camera origin (n=%d) o1 %f o0 %f o2 %f o_t1 %f o_t0 %f o_t2 %f\n",
// i,
// estimate[1],
// estimate[0],
// estimate[2],
// alignment_array[i](estimate)[1],
// alignment_array[i](estimate)[0],
// alignment_array[i](estimate)[2]);
ale_pos e0 = atan(-e_translated[1] / e_translated[2]);
ale_pos e1 = atan((double) e_translated[0]
/ ((double) e_translated[2] * cos(e0)
- (double) e_translated[1] * sin(e0)));
alignment_array[i].modify_rotation(0, e0);
alignment_array[i].modify_rotation(1, e1);
if (alignment_array[i](e)[2] > 0)
alignment_array[i].modify_rotation(1, M_PI);
// fprintf(stderr, "axis-intersection (n=%d) e1 %f e0 %f e_t1 %f e_t0 %f e_t2 %f\n",
// i,
// e[1],
// e[0],
// alignment_array[i](e)[1],
// alignment_array[i](e)[0],
// alignment_array[i](e)[2]);
//
// fprintf(stderr, "camera origin (n=%d) o_t1 %f o_t0 %f o_t2 %f\n",
// i,
// alignment_array[i](estimate)[1],
// alignment_array[i](estimate)[0],
// alignment_array[i](estimate)[2]);
point a_transformed = alignment_array[i](a);
ale_pos e2 = atan((t.scaled_width() - 1) / (t.scaled_height() - 1))
- atan(a_transformed[1] / a_transformed[0]);
// fprintf(stderr, "a components (n=%d) a1 %f a0 %f\n", i, a[1], a[0]);
// fprintf(stderr, "e2 components (n=%d) tw %f th %f a_t1 %f a_t0 %f\n", i,
// t.scaled_width(), t.scaled_height(), a_transformed[1], a_transformed[0]);
alignment_array[i].modify_rotation(2, e2);
// fprintf(stderr, "rotation (n=%d) e0 %f e1 %f e2 %f\n", i, e0, e1, e2);
if (alignment_array[i](a)[0] > 0) {
e2 += M_PI;
alignment_array[i].modify_rotation(2, M_PI);
// fprintf(stderr, "adding 2pi to e2.");
}
// fprintf(stderr, "rotation (n=%d) e0 %f e1 %f e2 %f\n", i, e0, e1, e2);
// fprintf(stderr, "(0, 0) output (n=%d) a1 %f a0 %f a_t1 %f a_t0 %f\n",
// i,
// a[1],
// a[0],
// alignment_array[i](a)[1],
// alignment_array[i](a)[0]);
assert (alignment_array[i](a)[0] < 0);
assert (alignment_array[i](a)[1] < 0);
}
}
static void adjust_translation(unsigned int n, int d, ale_pos x) {
alignment_array[n].modify_translation(d, x);
}
static void adjust_rotation(unsigned int n, int d, ale_pos x) {
alignment_array[n].modify_rotation(d, x);
}
static void set_translation(unsigned int n, int d, ale_pos x) {
alignment_array[n].set_translation(d, x);
}
static void set_rotation(unsigned int n, int d, ale_pos x) {
alignment_array[n].set_rotation(d, x);
}
static void adjust_view_angle(ale_pos x) {
_init_angle += x;
}
static void set_view_angle(ale_pos x) {
_init_angle = x;
}
};
#endif

47
d3/cpf.cc Normal file
View File

@@ -0,0 +1,47 @@
// Copyright 2005 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
/*
* d3/cpf.cc: Control point file handler static variables.
*/
#include "cpf.h"
FILE *cpf::load_f = NULL;
FILE *cpf::save_f = NULL;
int cpf::load_version = -1;
const char *cpf::load_n = NULL;
const char *cpf::save_n = NULL;
int cpf::save_version = 0;
struct cpf::control_point *cpf::cp_array = NULL;
unsigned int cpf::cp_array_max = 0;
unsigned int cpf::cp_index = 0;
ale_pos cpf::cpp_lower = 0.125;
ale_pos cpf::cpp_upper = 32;
ale_pos cpf::va_upper = 32;
ale_pos cpf::stereo_threshold = 4;
unsigned int cpf::systems_solved = 0;
int cpf::total_error_mean = 1;

605
d3/cpf.h Normal file
View File

@@ -0,0 +1,605 @@
// Copyright 2003, 2004, 2005 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
/*
* d3/cpf.h: Control point file interface.
*/
#ifndef __cpf_h__
#define __cpf_h__
#include "point.h"
#define CPF_VERSION 0
#define CPF_VERSION_MAX 0
class cpf {
static FILE *load_f;
static FILE *save_f;
static int load_version;
static const char *load_n;
static const char *save_n;
static int save_version;
static ale_pos cpp_upper;
static ale_pos va_upper;
static ale_pos cpp_lower;
static ale_pos stereo_threshold;
static int total_error_mean;
/*
* TYPE is:
* 0 type A
* 1 type B
* 2 type C
*/
struct control_point {
int type;
d2::point *d2;
point d3;
};
static struct control_point *cp_array;
static unsigned int cp_array_max;
static unsigned int cp_index;
static unsigned int systems_solved;
static void error(const char *message) {
fprintf(stderr, "cpf: Error: %s", message);
exit(1);
}
static void get_integer(int *i) {
if(fscanf(load_f, " %d", i) != 1)
error("Could not get integer.");
}
static void get_double(double *d) {
if (fscanf(load_f, " %lf", d) != 1)
error("Could not get double.");
}
static void get_new_line() {
int next_character = 0;
while (next_character != EOF
&& next_character != '\n')
next_character = fgetc(load_f);
}
static void check_version(int v) {
if (v > CPF_VERSION_MAX)
error("Incompatible version number.");
}
/*
* Type A control point record
*
* A <frame 0 coord 1> <frame 0 coord 0> ... <frame n coord 0>
*/
static void get_type_a() {
/*
* Get the number of frames.
*/
int n = d2::image_rw::count();
/*
* Allocate storage for N frames.
*/
d2::point *coords = new d2::point[n];
for (int i = 0; i < n; i++)
for (int j = 0; j < 2; j++) {
double dp;
get_double(&dp);
coords[i][1 - j] = dp;
}
cp_array[cp_array_max - 1].d2 = coords;
cp_array[cp_array_max - 1].d3 = point::undefined();
cp_array[cp_array_max - 1].type = 0;
}
/*
* Type B control point record
*
* B <coord 1> <coord 0> <coord 2>
*/
static void get_type_b() {
double d[3];
get_double(&d[1]);
get_double(&d[0]);
get_double(&d[2]);
cp_array[cp_array_max - 1].d3 = point(d[0], d[1], d[2]);
cp_array[cp_array_max - 1].type = 1;
}
/*
* Type C control point record
*
* C <type A data> <type B data>
*/
static void get_type_c() {
get_type_a();
get_type_b();
cp_array[cp_array_max - 1].type = 2;
}
/*
* Read a control point file.
*/
static void read_file() {
while (load_f && !feof(load_f)) {
int command_char;
command_char = fgetc(load_f);
switch (command_char) {
case EOF:
return;
case '#':
case ' ':
case '\t':
get_new_line();
break;
case '\r':
case '\n':
break;
case 'V':
get_integer(&load_version);
check_version(load_version);
get_new_line();
break;
case 'A':
cp_array = (control_point *) realloc(cp_array, ++cp_array_max * sizeof(control_point));
assert(cp_array);
get_type_a();
get_new_line();
break;
case 'B':
cp_array = (control_point *) realloc(cp_array, ++cp_array_max * sizeof(control_point));
assert(cp_array);
get_type_b();
get_new_line();
break;
case 'C':
cp_array = (control_point *) realloc(cp_array, ++cp_array_max * sizeof(control_point));
assert(cp_array);
get_type_c();
get_new_line();
break;
default:
error("Unrecognized command");
}
}
}
/*
* Calculate the centroid of a set of points.
*/
static point calculate_centroid(point *points, int n) {
point sum(0, 0, 0);
int count = 0;
for (int i = 0; i < n; i++) {
if (!points[i].defined())
continue;
sum += points[i];
count++;
}
return sum / count;
}
/*
* Measure the error between a projected system and a solved coordinate.
*/
static ale_real measure_projected_error(point solved, const d2::point coords[], int n) {
ale_accum error = 0;
ale_accum divisor = 0;
for (int i = 0; i < n; i++) {
if (!coords[i].defined())
continue;
pt t = align::projective(i);
point sp = t.wp_unscaled(solved);
error += (sp.xy() - coords[i]).normsq();
divisor += 1;
}
return sqrt(error / divisor);
}
static ale_accum measure_total_error() {
if (cp_array_max == 0)
return 0;
if (total_error_mean) {
/*
* Use mean error.
*/
ale_accum result = 0;
ale_accum divisor = 0;
for (unsigned int i = 0; i < cp_array_max; i++) {
ale_accum e = measure_projected_error(cp_array[i].d3, cp_array[i].d2, d2::image_rw::count());
if (!finite(e))
continue;
result += e * e;
divisor += 1;
}
return sqrt(result / divisor);
} else {
/*
* Use median error
*/
std::vector<ale_accum> v;
for (unsigned int i = 0; i < cp_array_max; i++) {
ale_accum e = measure_projected_error(cp_array[i].d3, cp_array[i].d2, d2::image_rw::count());
if (!finite(e))
continue;
v.push_back(fabs(e));
}
std::sort(v.begin(), v.end());
if (v.size() % 2) {
return v[v.size() / 2];
} else {
return 0.5 * (v[v.size() / 2 - 1]
+ v[v.size() / 2]);
}
}
}
/*
* Solve for a 3D point from a system of projected coordinates.
*
* The algorithm is this:
*
* First, convert all 2D points to 3D points by projecting them onto a
* plane perpendicular to the view axis of the corresponding camera,
* and passing through the origin of the world coordinate system.
* Then, for each point, move it along the view ray to the point
* closest to the centroid of all of the points. Repeat this last loop
* until the largest adjustment is smaller than some specified lower
* bound.
*/
static point solve_projected_system(const d2::point points[], int n) {
/*
* Copy the passed array.
*/
point *points_copy = new point[n];
for (int i = 0; i < n; i++)
points_copy[i] = point(points[i][0], points[i][1], 0);
/*
* Set an initial depth for each point, and convert it to world
* coordinates.
*/
for (int i = 0; i < n; i++) {
pt t = align::projective(i);
point p = t.wc(point(0, 0, 0));
ale_pos plane_depth = p[2];
points_copy[i][2] = plane_depth;
points_copy[i] = t.pw_unscaled(points_copy[i]);
}
/*
* For each point, adjust the depth along the view ray to
* minimize the distance from the centroid of the points_copy.
*/
ale_pos max_diff = 0;
ale_pos prev_max_diff = 0;
ale_pos diff_bound = 0.99999;
while (!(max_diff / prev_max_diff > diff_bound) || !finite(max_diff / prev_max_diff)) {
/*
* Calculate the centroid of all points_copy.
*/
point centroid = calculate_centroid(points_copy, n);
// fprintf(stderr, "md %f pmd %f ratio %f ", max_diff, prev_max_diff, max_diff / prev_max_diff);
// fprintf(stderr, "centroid %f %f %f ", centroid[0], centroid[1], centroid[2]);
// fprintf(stderr, "%f ", measure_projected_error(centroid, points, n));
prev_max_diff = max_diff;
max_diff = 0;
for (int i = 0; i < n; i++) {
// fprintf(stderr, "points_copy[%d] %f %f %f ", i, points_copy[i][0], points_copy[i][1], points_copy[i][2]);
if (!points_copy[i].defined())
continue;
pt t = align::projective(i);
point camera_origin = t.cw(point(0, 0, 0));
point v = points_copy[i] - camera_origin;
ale_pos l = (centroid - camera_origin).norm();
ale_pos alpha = camera_origin.anglebetw(points_copy[i], centroid);
point new_point = camera_origin + v / v.norm() * l * cos(alpha);
ale_pos diff = points_copy[i].lengthto(new_point);
if (diff > max_diff)
max_diff = diff;
points_copy[i] = new_point;
}
}
// fprintf(stderr, "%f\n", measure_projected_error(calculate_centroid(points_copy, n), points, n));
// fprintf(stderr, "md %f pmd %f ratio %f ", max_diff, prev_max_diff, max_diff / prev_max_diff);
point result = calculate_centroid(points_copy, n);
delete[] points_copy;
return result;
}
static void solve_total_system() {
for (unsigned i = 0; i < cp_array_max; i++) {
if (cp_array[i].type == 0)
cp_array[i].d3 = solve_projected_system(cp_array[i].d2, d2::image_rw::count());
}
}
public:
static void err_median() {
total_error_mean = 0;
}
static void err_mean() {
total_error_mean = 1;
}
static void set_cpp_upper(ale_pos cu) {
cpp_upper = cu;
}
static void set_cpp_lower(ale_pos cl) {
cpp_lower = cl;
}
static void set_va_upper(ale_pos vau_degrees) {
va_upper = (double) vau_degrees * M_PI / 180;
}
static void init_loadfile(const char *filename) {
load_n = filename;
load_f = fopen(load_n, "r");
if (!load_f) {
fprintf(stderr, "cpf: Error: could not open control point file '%s'.", load_n);
exit(1);
}
}
static void init_savefile(const char *filename) {
save_n = filename;
save_f = fopen(save_n, "w");
if (!save_f) {
fprintf(stderr, "cpf: Error: could not open control point file '%s'.", save_n);
exit(1);
}
fprintf(save_f, "# created by ALE control point file handler version %d\n", CPF_VERSION);
fclose(save_f);
}
static void adjust_cameras() {
ale_accum current_error = measure_total_error();
unsigned int n = d2::image_rw::count();
/*
* Perturbation measured in pixels or degrees
*
* XXX: should probably be pixel arclength instead of degrees.
*/
ale_pos max_perturbation = pow(2, floor(log(cpp_upper) / log(2)));
ale_pos min_perturbation = cpp_lower;
ale_pos perturbation = max_perturbation;
ui::get()->d3_control_point_data(max_perturbation, min_perturbation, perturbation, current_error);
/*
* XXX: Does this ever execute?
*/
if (cp_array_max == 0) {
fprintf(stderr, " (no points specified)");
return;
}
if (perturbation < min_perturbation || cp_array_max == 0) {
// fprintf(stderr, " (skipping adjustment)");
return;
}
while (perturbation >= min_perturbation) {
ale_accum previous_error;
ale_pos angular_p = (double) perturbation / 180 * M_PI;
// fprintf(stderr, "P %f AP %f ", perturbation, angular_p);
do {
/*
* Minimum frame to adjust
*/
int M = 1;
previous_error = current_error;
ale_accum test_error;
/*
* Try adjusting camera positions
*/
for (unsigned int i = M; i < n; i++)
for (unsigned int d = 0; d < 3; d++)
for ( int s = -1; s <= 1; s+=2) {
align::adjust_translation(i, d, s * perturbation);
solve_total_system();
test_error = measure_total_error();
if (test_error < current_error) {
current_error = test_error;
} else {
align::adjust_translation(i, d, -s * perturbation);
}
}
/*
* Try adjusting camera orientations
*/
for (unsigned int i = M; i < n; i++)
for (unsigned int d = 0; d < 3; d++)
for ( int s = -1; s <= 1; s+=2) {
align::adjust_rotation(i, d, s * angular_p);
solve_total_system();
test_error = measure_total_error();
if (test_error < current_error) {
current_error = test_error;
} else {
align::adjust_rotation(i, d, -s * angular_p);
}
}
/*
* Try adjusting view angle
*/
if (angular_p <= va_upper)
for (int s = -1; s <= 1; s+=2) {
align::adjust_view_angle(s * angular_p);
solve_total_system();
test_error = measure_total_error();
if (test_error < current_error) {
current_error = test_error;
} else {
align::adjust_view_angle(-s * angular_p);
}
}
} while (current_error < previous_error);
// fprintf(stderr, "E %f\n", current_error);
perturbation /= 2;
ui::get()->d3_control_point_data(max_perturbation, min_perturbation, perturbation, current_error);
ui::get()->d3_control_point_step();
}
solve_total_system();
}
static void st(ale_pos _st) {
stereo_threshold = _st;
}
static void solve_3d() {
if (systems_solved)
return;
solve_total_system();
adjust_cameras();
systems_solved = 1;
}
static unsigned int count() {
if (cp_array_max == 0)
read_file();
return cp_array_max;
}
static const d2::point *get_2d(unsigned int index) {
assert (index < cp_array_max);
assert (cp_array != NULL);
if (cp_array[index].type == 1)
return NULL;
return cp_array[index].d2;
}
static point get(unsigned int index) {
assert (index < cp_array_max);
assert (cp_array != NULL);
if (!systems_solved)
solve_3d();
if (stereo_threshold < (ale_pos) measure_projected_error(cp_array[index].d3,
cp_array[index].d2,
d2::image_rw::count()))
return point::undefined();
return cp_array[index].d3;
}
static void set(point p) {
assert(0);
}
static void finish_loadfile() {
}
static void finish_savefile() {
}
};
#endif

383
d3/et.h Normal file
View File

@@ -0,0 +1,383 @@
// Copyright 2003 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
/*
* d3/et.h: Represent three-dimensional Euclidean transformations.
*/
#ifndef __et_h__
#define __et_h__
#include "point.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/*
* Structure to describe a three-dimensional euclidean transformation.
*
* We consider points to be transformed as homogeneous coordinate vectors
* multiplied on the right of the transformation matrix. The transformations
* are assumed to be small, so xyz Euler angles are used to specify rotation.
* The order of transformations is (starting with a point representation in the
* original coordinate system):
*
* i) translation of the point
* ii) x-rotation about the origin
* iii) y-rotation about the origin
* iv) z-rotation about the origin
*
* For more information on Euler angles, see:
*
* http://mathworld.wolfram.com/EulerAngles.html
*
* For more information on rotation matrices, see:
*
* http://mathworld.wolfram.com/RotationMatrix.html
*
* XXX: inconsistently with the 2D class, this class uses radians to represent
* rotation values.
*
*/
struct et {
private:
ale_pos translation[3];
ale_pos rotation[3];
mutable ale_pos matrix[4][4];
mutable ale_pos matrix_inverse[4][4];
mutable int resultant_memo;
mutable int resultant_inverse_memo;
/*
* Create an identity matrix
*/
void mident(ale_pos m1[4][4]) const {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
m1[i][j] = (i == j) ? 1 : 0;
}
/*
* Create a rotation matrix
*/
void mrot(ale_pos m1[4][4], ale_pos angle, int index) const {
mident(m1);
m1[(index+1)%3][(index+1)%3] = cos(angle);
m1[(index+2)%3][(index+2)%3] = cos(angle);
m1[(index+1)%3][(index+2)%3] = sin(angle);
m1[(index+2)%3][(index+1)%3] = -sin(angle);
}
/*
* Multiply matrices M1 and M2; return result M3.
*/
void mmult(ale_pos m3[4][4], ale_pos m1[4][4], ale_pos m2[4][4]) const {
int k;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
for (m3[i][j] = 0, k = 0; k < 4; k++)
m3[i][j] += m1[i][k] * m2[k][j];
}
/*
* Calculate resultant matrix values.
*/
void resultant() const {
/*
* If we already know the answers, don't bother calculating
* them again.
*/
if (resultant_memo)
return;
/*
* Multiply matrices.
*/
ale_pos m1[4][4], m2[4][4], m3[4][4];
/*
* Translation
*/
mident(m1);
for (int i = 0; i < 3; i++)
m1[i][3] = translation[i];
/*
* Rotation about x
*/
mrot(m2, rotation[0], 0);
mmult(m3, m2, m1);
/*
* Rotation about y
*/
mrot(m1, rotation[1], 1);
mmult(m2, m1, m3);
/*
* Rotation about z
*/
mrot(m3, rotation[2], 2);
mmult(matrix, m3, m2);
resultant_memo = 1;
}
/*
* Calculate the inverse transform matrix values.
*/
void resultant_inverse () const {
/*
* If we already know the answers, don't bother calculating
* them again.
*/
if (resultant_inverse_memo)
return;
/*
* The inverse can be explicitly calculated from the rotation
* and translation parameters. We invert the individual
* matrices and reverse the order of multiplication.
*/
ale_pos m1[4][4], m2[4][4], m3[4][4];
/*
* Translation
*/
mident(m1);
for (int i = 0; i < 3; i++)
m1[i][3] = -translation[i];
/*
* Rotation about x
*/
mrot(m2, -rotation[0], 0);
mmult(m3, m1, m2);
/*
* Rotation about y
*/
mrot(m1, -rotation[1], 1);
mmult(m2, m3, m1);
/*
* Rotation about z
*/
mrot(m3, -rotation[2], 2);
mmult(matrix_inverse, m2, m3);
resultant_inverse_memo = 1;
}
public:
/*
* Transform point p.
*/
struct point transform(struct point p) const {
struct point result;
resultant();
for (int i = 0; i < 3; i++) {
for (int k = 0; k < 3; k++)
result[i] += matrix[i][k] * p[k];
result[i] += matrix[i][3];
}
return result;
}
/*
* operator() is the transformation operator.
*/
struct point operator()(struct point p) const {
return transform(p);
}
/*
* Map point p using the inverse of the transform.
*/
struct point inverse_transform(struct point p) const {
struct point result;
resultant_inverse();
for (int i = 0; i < 3; i++) {
for (int k = 0; k < 3; k++)
result[i] += matrix_inverse[i][k] * p[k];
result[i] += matrix_inverse[i][3];
}
return result;
}
/*
* Default constructor
*
* Returns the identity transformation.
*
* Note: identity() depends on this.
*/
et() {
resultant_memo = 0;
resultant_inverse_memo = 0;
translation[0] = 0;
translation[1] = 0;
translation[2] = 0;
rotation[0] = 0;
rotation[1] = 0;
rotation[2] = 0;
}
et(ale_pos x, ale_pos y, ale_pos z, ale_pos P, ale_pos Y, ale_pos R) {
resultant_memo = 0;
resultant_inverse_memo = 0;
translation[0] = x;
translation[1] = y;
translation[2] = z;
rotation[0] = P;
rotation[1] = Y;
rotation[2] = R;
}
/*
* Return identity transformation.
*/
static struct et identity() {
struct et r;
return r;
}
/*
* Set Euclidean parameters (x, y, z, P, Y, R).
*/
void set(double values[6]) {
for (int i = 0; i < 3; i++)
translation[i] = values[i];
for (int i = 0; i < 3; i++)
rotation[i] = values[i + 3];
}
/*
* Adjust translation in the indicated manner.
*/
void modify_translation(int i1, ale_pos diff) {
assert (i1 >= 0);
assert (i1 < 3);
resultant_memo = 0;
resultant_inverse_memo = 0;
translation[i1] += diff;
}
void set_translation(int i1, ale_pos new_value) {
assert (i1 >= 0);
assert (i1 < 3);
resultant_memo = 0;
resultant_inverse_memo = 0;
translation[i1] = new_value;
}
/*
* Adjust rotation in the indicated manner.
*/
void modify_rotation(int i1, ale_pos diff) {
assert (i1 >= 0);
assert (i1 < 3);
resultant_memo = 0;
resultant_inverse_memo = 0;
rotation[i1] += diff;
}
void set_rotation(int i1, ale_pos new_value) {
assert (i1 >= 0);
assert (i1 < 3);
resultant_memo = 0;
resultant_inverse_memo = 0;
rotation[i1] = new_value;
}
ale_pos get_rotation(int i1) {
assert (i1 >= 0);
assert (i1 < 3);
return rotation[i1];
}
ale_pos get_translation(int i1) {
assert (i1 >= 0);
assert (i1 < 3);
return translation[i1];
}
void debug_output() {
fprintf(stderr, "[et.do t=[%f %f %f] r=[%f %f %f]\n"
" m=[[%f %f %f %f] [%f %f %f %f] [%f %f %f %f] [%f %f %f %f]]\n"
" mi=[[%f %f %f %f] [%f %f %f %f] [%f %f %f %f] [%f %f %f %f]]\n"
" rm=%d rim=%d]\n",
(double) translation[0], (double) translation[1], (double) translation[2],
(double) rotation[0], (double) rotation[1], (double) rotation[2],
(double) matrix[0][0], (double) matrix[0][1], (double) matrix[0][2], (double) matrix[0][3],
(double) matrix[1][0], (double) matrix[1][1], (double) matrix[1][2], (double) matrix[1][3],
(double) matrix[2][0], (double) matrix[2][1], (double) matrix[2][2], (double) matrix[2][3],
(double) matrix[3][0], (double) matrix[3][1], (double) matrix[3][2], (double) matrix[3][3],
(double) matrix_inverse[0][0], (double) matrix_inverse[0][1], (double) matrix_inverse[0][2], (double) matrix_inverse[0][3],
(double) matrix_inverse[1][0], (double) matrix_inverse[1][1], (double) matrix_inverse[1][2], (double) matrix_inverse[1][3],
(double) matrix_inverse[2][0], (double) matrix_inverse[2][1], (double) matrix_inverse[2][2], (double) matrix_inverse[2][3],
(double) matrix_inverse[3][0], (double) matrix_inverse[3][1], (double) matrix_inverse[3][2], (double) matrix_inverse[3][3],
resultant_memo, resultant_inverse_memo);
}
};
#endif

31
d3/focus.cc Normal file
View File

@@ -0,0 +1,31 @@
// Copyright 2003, 2004, 2005 David Hilvert <dhilvert@gmail.com>,
// <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
#include "focus.h"
/*
* See focus.h for details regarding these variables.
*/
unsigned int focus::camera_index;
std::vector<std::vector<focus::entry> > focus::focus_list;
unsigned int focus::_uses_medians = 0;
unsigned int focus::_max_samples = 0;

178
d3/focus.h Normal file
View File

@@ -0,0 +1,178 @@
// Copyright 2006 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
/*
* d3/focus.h: Implementation of defocusing logic.
*/
#ifndef __focus_h__
#define __focus_h__
class focus {
private:
struct entry {
int type;
double distance;
double px, py;
double focal_range;
double vertical_gradient;
double horizontal_gradient;
double start_depth;
double end_depth;
double start_x;
double end_x;
double start_y;
double end_y;
double aperture;
unsigned int sample_count;
unsigned int focal_statistic;
unsigned int sample_randomization;
};
static unsigned int _uses_medians;
static unsigned int _max_samples;
static unsigned int camera_index;
static std::vector<std::vector<entry> > focus_list;
public:
struct result {
double focal_distance;
double aperture;
unsigned int sample_count;
unsigned int statistic;
unsigned int randomization;
};
static void add_region(unsigned int type, double distance, double px, double py,
unsigned int ci, double fr, double ht, double vt, double sd, double ed,
double sx, double ex, double sy, double ey, double ap, unsigned int sc, unsigned int fs,
unsigned int sr) {
if (fs)
_uses_medians = 1;
if (sc > _max_samples)
_max_samples = sc;
if (focus_list.size() <= ci)
focus_list.resize(ci + 1);
entry e = { type, distance, px, py, fr, vt, ht, sd, ed, sx, ex, sy, ey, ap, sc, fs, sr };
focus_list[ci].push_back(e);
}
static int is_trivial() {
return (focus_list.size() == 0);
}
static int uses_medians() {
return _uses_medians;
}
static unsigned int max_samples() {
return _max_samples;
}
static result get(const d2::image *depth, int i, int j) {
ale_pos d = (double) depth->get_pixel(i, j)[0];
std::vector<entry> *l = &(focus_list[camera_index]);
/*
* Initialize default focus result.
*/
result r = { d, 0, 1, 0, 0 };
/*
* Check for relevant focus regions.
*/
for (unsigned int n = 0; n < l->size(); n++) {
entry *e = &((*l)[n]);
if (i >= e->start_y
&& i <= e->end_y
&& j >= e->start_x
&& j <= e->end_x
&& ((d >= -e->end_depth
&& d <= -e->start_depth)
|| (isnan(d) && (isnan(e->end_depth)
|| isnan(e->start_depth))))) {
d2::point focus_origin;
ale_pos distance_at_focus_origin;
if (e->type == 0) {
/*
* Distance at frame center.
*/
focus_origin = d2::point(depth->height() / 2, depth->width() / 2);
distance_at_focus_origin = -e->distance;
} else if (e->type == 1) {
/*
* Distance at a given point.
*/
focus_origin = d2::point(e->py, e->px);
distance_at_focus_origin = (double) depth->get_bl(d2::point(e->py, e->px))[0];
} else {
fprintf(stderr, "Bad entry type.\n");
assert(0);
exit(1);
}
r.focal_distance = distance_at_focus_origin + (d2::point(i, j) - focus_origin)
.dproduct(d2::point(-e->vertical_gradient,
-e->horizontal_gradient));
/*
* Adjust according to focal_range.
*/
ale_pos rel_dist = (double) d - r.focal_distance;
if (fabs(rel_dist) < e->focal_range / 2) {
r.focal_distance = d;
} else if (rel_dist > 0) {
r.focal_distance += e->focal_range / 2;
} else if (rel_dist < 0) {
r.focal_distance -= e->focal_range / 2;
}
r.aperture = e->aperture;
r.sample_count = e->sample_count;
r.statistic = e->focal_statistic;
r.randomization = e->sample_randomization;
break;
}
}
return r;
}
static void set_camera(unsigned int c) {
camera_index = c;
}
};
#endif

295
d3/point.h Normal file
View File

@@ -0,0 +1,295 @@
// Copyright 2002, 2004 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
#ifndef __d3point_h__
#define __d3point_h__
/*
* Structure to describe a point in three dimensions.
*/
class point {
private:
ale_pos x[3];
public:
point() {
x[0] = 0;
x[1] = 0;
x[2] = 0;
}
point(ale_pos x0, ale_pos x1, ale_pos x2) {
x[0] = x0;
x[1] = x1;
x[2] = x2;
}
point(const point &p) {
x[0] = p[0];
x[1] = p[1];
x[2] = p[2];
}
static point unit(int dimension) {
if (dimension == 0)
return point(1, 0, 0);
if (dimension == 1)
return point(0, 1, 0);
if (dimension == 2)
return point(0, 0, 1);
assert(0);
}
static point undefined() {
double a = 0;
point p(0, 0, 0);
return p / a;
}
static point posinf() {
double a = +1;
double z = +0;
a = a / z;
assert (isinf(a) && a > 0);
return point(a, a, a);
}
static point neginf() {
point n = -posinf();
assert (isinf(n[0]) && n[0] < 0);
return n;
}
void accumulate_max(point p) {
for (int d = 0; d < 3; d++)
if (p[d] > x[d])
x[d] = p[d];
}
void accumulate_min(point p) {
for (int d = 0; d < 3; d++)
if (p[d] < x[d])
x[d] = p[d];
}
int defined() const {
return (!isnan(x[0])
&& !isnan(x[1])
&& !isnan(x[2]));
}
int finite() const {
return (::finite(x[0])
&& ::finite(x[1])
&& ::finite(x[2]));
}
static int defined(const point &p) {
return p.defined();
}
/*
* Z-values of zero are almost never the right thing to do ...
*/
point(d2::point p, ale_pos z = 0) {
x[0] = p[0];
x[1] = p[1];
x[2] = z;
}
const ale_pos &operator[](int i) const {
assert (i >= 0);
assert (i < 3);
return x[i];
}
ale_pos &operator[](int i) {
assert (i >= 0);
assert (i < 3);
return x[i];
}
d2::point xy() const {
d2::point result;
result[0] = x[0];
result[1] = x[1];
return result;
}
point operator+(point p) const {
return point(x[0] + p[0], x[1] + p[1], x[2] + p[2]);
}
point operator-(point p) const {
return point(x[0] - p[0], x[1] - p[1], x[2] - p[2]);
}
point operator-() const {
return point(-x[0], -x[1], -x[2]);
}
point operator/(ale_pos r) const {
return point(x[0] / r, x[1] / r, x[2] / r);
}
point operator /=(ale_pos r) {
x[0] /= r;
x[1] /= r;
x[2] /= r;
return *this;
}
point operator *=(ale_pos r) {
x[0] *= r;
x[1] *= r;
x[2] *= r;
return *this;
}
point operator +=(point p) {
x[0] += p[0];
x[1] += p[1];
x[2] += p[2];
return *this;
}
point operator -=(point p) {
x[0] -= p[0];
x[1] -= p[1];
x[2] -= p[2];
return *this;
}
int operator !=(point p) {
return (x[0] != p[0]
|| x[1] != p[1]
|| x[2] != p[2]);
}
point mult(ale_pos d) const {
return point(x[0] * d, x[1] * d, x[2] * d);
}
point operator*(point p) const {
return point(x[0] * p[0], x[1] * p[1], x[2] * p[2]);
}
ale_pos normsq() const {
return x[0] * x[0] + x[1] * x[1] + x[2] * x[2];
}
ale_pos norm() const {
return sqrt(normsq());
}
point normalize() const {
return operator/(norm());
}
ale_pos lengthtosq(point p) const {
point diff = operator-(p);
return diff.normsq();
}
ale_pos lengthto(point p) const {
return sqrt(lengthtosq(p));
}
ale_pos anglebetw(point p, point q) {
/*
* by the law of cosines, the cosine is equal to:
*
* (lengthtosq(p) + lengthtosq(q) - p.lengthtosq(q))
* / (2 * lengthto(p) * lengthto(q))
*/
ale_pos to_p = lengthtosq(p);
ale_pos to_q = lengthtosq(q);
ale_pos cos_of = (double) (to_p + to_q - p.lengthtosq(q))
/ (double) (2 * sqrt(to_p) * sqrt(to_q));
/*
* XXX: is the fabs() required?
*/
return fabs(acos(cos_of));
}
/*
* Determine the cross product
*/
point xproduct(point p, point q) {
point pp = p;
point qq = q;
pp -= *this;
qq -= *this;
return point(pp[1] * qq[2] - pp[2] * qq[1],
pp[2] * qq[0] - pp[0] * qq[2],
pp[0] * qq[1] - pp[1] * qq[0]);
}
/*
* Determine the dot product
*/
ale_pos dproduct(const point &p) {
return x[0] * p[0] + x[1] * p[1] + x[2] * p[2];
}
/*
* Determine whether the point is inside a given volume
*/
int inside(const point &min, const point &max) {
for (int d = 0; d < 3; d++) {
if (min[d] > x[d])
return 0;
if (max[d] < x[d])
return 0;
}
}
};
inline point operator*(const point &p, double d) {
return p.mult(d);
}
inline point operator*(double d, const point &p) {
return p.mult(d);
}
#endif

545
d3/pt.h Normal file
View File

@@ -0,0 +1,545 @@
// Copyright 2005 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
/*
* d3/et.h: Represent 3D->2D projective transformations.
*/
#ifndef __pt_h__
#define __pt_h__
#include "space.h"
#include "et.h"
/*
* Structure to describe a 3D->2D projective transformation. 3D information is
* preserved by adding a depth element to the result.
*
* The following coordinate systems are considered:
*
* P: projective
* C: local cartesian
* W: world
*/
struct pt {
private:
d2::transformation t;
et euclidean;
ale_pos _view_angle;
ale_pos scale_factor;
mutable ale_pos diag_per_depth;
public:
/*
* Constructor
*/
pt() : t(d2::transformation::eu_identity()) {
_view_angle = M_PI / 4;
scale_factor = 1;
diag_per_depth = 0;
}
pt(d2::transformation t, et e, ale_pos va, ale_pos sf = 1) : t(t) {
this->t = t;
euclidean = e;
_view_angle = va;
scale_factor = sf;
diag_per_depth = 0;
}
/*
* Output function
*/
void debug_output() {
t.debug_output();
euclidean.debug_output();
fprintf(stderr, "[pt.do va=%f sf=%f dpd=%f\n]",
(double) _view_angle, (double) scale_factor, (double) diag_per_depth);
}
/*
* Get euclidean transformation reference.
*/
et &e() {
return euclidean;
}
/*
* Modify scale factor
*/
void scale(ale_pos sf) {
scale_factor = sf;
}
/*
* Modify or get view angle
*/
void view_angle(ale_pos va) {
diag_per_depth = 0;
_view_angle = va;
}
ale_pos view_angle() {
return _view_angle;
}
/*
* Get the 2D scale factor
*/
ale_pos scale_2d() const {
return t.scale();
}
/*
* Transform W to C.
*/
point wc(point p) const {
return euclidean(p);
}
/*
* Transform C to P for given width and height.
*/
point cp_generic(point p, ale_pos w, ale_pos h) const {
/*
* Divide x and y by negative z
*/
p[0] /= -p[2];
p[1] /= -p[2];
/*
* Scale x and y
*/
ale_pos scaling_factor = (ale_pos) sqrt(w*w + h*h)
/ (ale_pos) (2 * tan(_view_angle / 2));
p[0] *= scaling_factor;
p[1] *= scaling_factor;
/*
* Add an offset so that the upper-left corner is the origin.
*/
p[0] += h / 2;
p[1] += w / 2;
return p;
}
/*
* Transform point p.
*/
struct point wp_generic(struct point p, ale_pos w, ale_pos h) const {
return cp_generic(wc(p), w, h);
}
/*
* Width and height
*/
ale_pos scaled_width() const {
return t.scaled_width() * scale_factor;
}
ale_pos scaled_height() const {
return t.scaled_height() * scale_factor;
}
int scaled_in_bounds(point p) const {
return (p[0] >= 0 && p[0] <= scaled_height() - 1
&& p[1] >= 0 && p[1] <= scaled_width() - 1);
}
int unscaled_in_bounds(point p) const {
return (p[0] >= 0 && p[0] <= unscaled_height() - 1
&& p[1] >= 0 && p[1] <= unscaled_width() - 1);
}
ale_pos unscaled_width() const {
return t.unscaled_width() * scale_factor;
}
ale_pos unscaled_height() const {
return t.unscaled_height() * scale_factor;
}
/*
* Scaled transforms
*/
point cp_scaled(point p) const {
return cp_generic(p, scaled_width(), scaled_height());
}
point wp_scaled(point p) const {
return wp_generic(p, scaled_width(), scaled_height());
}
/*
* Unscaled transforms
*/
point cp_unscaled(point p) const {
return cp_generic(p, unscaled_width(), unscaled_height());
}
point wp_unscaled(point p) const {
return wp_generic(p, unscaled_width(), unscaled_height());
}
/*
* Transform P to C.
*/
point pc_generic(point p, ale_pos w, ale_pos h) const {
/*
* Subtract offset
*/
p[0] -= h / 2;
p[1] -= w / 2;
/*
* Scale x and y
*/
ale_pos scaling_factor = (ale_pos) sqrt(w*w + h*h)
/ (ale_pos) (2 * tan(_view_angle / 2));
p[0] /= scaling_factor;
p[1] /= scaling_factor;
/*
* Multiply x and y by negative z
*/
p[0] *= -p[2];
p[1] *= -p[2];
return p;
}
/*
* Transform C to W
*/
point cw(point p) const {
return euclidean.inverse_transform(p);
}
/*
* Transform P to W
*/
point pw_generic(point p, ale_pos w, ale_pos h) const {
return cw(pc_generic(p, w, h));
}
/*
* Inverse transforms for scaled points.
*/
point pc_scaled(point p) const {
return pc_generic(p, scaled_width(), scaled_height());
}
point pw_scaled(point p) const {
return pw_generic(p, scaled_width(), scaled_height());
}
/*
* Inverse transforms for unscaled points.
*/
point pc_unscaled(point p) const {
return pc_generic(p, unscaled_width(), unscaled_height());
}
point pw_unscaled(point p) const {
return pw_generic(p, unscaled_width(), unscaled_height());
}
/*
* Density calculation
*/
ale_pos c_density_scaled(point p) const {
ale_pos one_density = 1 / (pc_scaled(point(0, 0, -1)).lengthto(pc_scaled(point(0, 1, -1)))
* pc_scaled(point(0, 0, -1)).lengthto(pc_scaled(point(1, 0, -1))));
return one_density / (p[2] * p[2]);
}
ale_pos w_density_scaled(point p) const {
return c_density_scaled(wc(p));
}
ale_pos w_density_scaled_max(point w0, point w1, point w2) {
point c0 = wc(w0);
point c1 = wc(w1);
point c2 = wc(w2);
/*
* Select the point closest to the camera.
*/
if (c0[2] > c1[2] && c0[2] > c2[2])
return c_density_scaled(c0);
else if (c1[2] > c2[2])
return c_density_scaled(c1);
else
return c_density_scaled(c2);
}
private:
void calculate_diag_per_depth() const {
if (diag_per_depth != 0)
return;
ale_pos w = unscaled_width();
ale_pos h = unscaled_height();
diag_per_depth = sqrt(2) * (2 * tan(_view_angle / 2)) / sqrt(w*w + h*h);
}
public:
/*
* Get a trilinear coordinate for a given depth.
*/
ale_pos trilinear_coordinate(ale_pos depth, ale_pos diagonal) {
calculate_diag_per_depth();
return log(diagonal / (fabs(depth) * diag_per_depth)) / log(2);
}
/*
* Get a trilinear coordinate for a given position in the world and
* a given 2D diagonal distance.
*/
ale_pos trilinear_coordinate(point w, ale_pos diagonal) {
return trilinear_coordinate(wc(w)[2], diagonal);
}
/*
* Get a trilinear coordinate for a given subspace.
*/
ale_pos trilinear_coordinate(const space::traverse &st) {
point min = st.get_min();
point max = st.get_max();
point avg = (min + max) / (ale_pos) 2;
ale_pos diagonal = min.lengthto(max) * (ale_pos) (sqrt(2) / sqrt(3));
return trilinear_coordinate(avg, diagonal);
}
/*
* Get a diagonal distance for a given position in the world
* and a given trilinear coordinate.
*/
ale_pos diagonal_distance(point w, ale_pos coordinate) const {
calculate_diag_per_depth();
ale_pos depth = fabs(wc(w)[2]);
ale_pos diagonal = pow(2, coordinate) * depth * diag_per_depth;
return diagonal;
}
/*
* Get the 3D diagonal for a given depth and trilinear coordinate.
*/
ale_pos diagonal_distance_3d(ale_pos depth, ale_pos coordinate) const {
calculate_diag_per_depth();
return pow(2, coordinate) * fabs(depth) * diag_per_depth * (ale_pos) (sqrt(3) / sqrt(2));
}
/*
* Get the 1D distance for a given depth and trilinear coordinate.
*/
ale_pos distance_1d(ale_pos depth, ale_pos coordinate) const {
calculate_diag_per_depth();
return pow(2, coordinate) * fabs(depth) * diag_per_depth / (ale_pos) (sqrt(2));
}
ale_pos distance_1d(point iw, ale_pos coordinate) const {
if (wc(iw)[2] >= 0)
return point::undefined()[0];
return distance_1d(-wc(iw)[2], coordinate);
}
/*
* Check for inclusion of a point in the bounding box of projected
* vertices. This function returns non-zero when a point is included,
* when one of the vertices is infinite or undefined, or when a vertex
* is behind the point of projection.
*
* WBB is assumed to contain {volume_min, volume_max}.
*/
int check_inclusion(const point *wbb, const d2::point &pc_min, const d2::point &pc_max, int scaled) const {
assert(pc_min[0] <= pc_max[0]);
assert(pc_min[1] <= pc_max[1]);
int test[2][2] = {{0, 0}, {0, 0}};
for (int x = 0; x < 2; x++)
for (int y = 0; y < 2; y++)
for (int z = 0; z < 2; z++) {
point p = scaled ? wp_scaled(point(wbb[x][0], wbb[y][1], wbb[z][2]))
: wp_unscaled(point(wbb[x][0], wbb[y][1], wbb[z][2]));
if (!p.finite())
return 1;
if (p[2] > 0)
return 1;
for (int d = 0; d < 2; d++) {
if (p[d] <= pc_max[d])
test[d][0] = 1;
if (p[d] >= pc_min[d])
test[d][1] = 1;
}
}
for (int d = 0; d < 2; d++)
for (int c = 0; c < 2; c++)
if (test[d][c] == 0)
return 0;
return 1;
}
int check_inclusion_scaled(const point *wbb, d2::point pc_min, d2::point pc_max) const {
return check_inclusion(wbb, pc_min, pc_max, 1);
}
int check_inclusion_scaled(const space::traverse &st, d2::point pc_min, d2::point pc_max) const {
return check_inclusion_scaled(st.get_bounds(), pc_min, pc_max);
}
int check_inclusion_scaled(const space::traverse &st, d2::point pc) {
return check_inclusion_scaled(st, pc, pc);
}
/*
* Get bounding box for projection of a subspace.
*/
void get_view_local_bb(point volume_min, point volume_max, point bb[2], int scaled) const {
point min = point::posinf();
point max = point::neginf();
point wbb[2] = { volume_min, volume_max };
for (int x = 0; x < 2; x++)
for (int y = 0; y < 2; y++)
for (int z = 0; z < 2; z++) {
point p = scaled ? wp_scaled(point(wbb[x][0], wbb[y][1], wbb[z][2]))
: wp_unscaled(point(wbb[x][0], wbb[y][1], wbb[z][2]));
for (int d = 0; d < 3; d++) {
if (p[d] < min[d])
min[d] = p[d];
if (p[d] > max[d])
max[d] = p[d];
}
}
/*
* Clip bounding box to image extents.
*/
if (min[0] < 0)
min[0] = 0;
if (min[1] < 0)
min[1] = 0;
if (max[0] > scaled_height() - 1)
max[0] = scaled_height() - 1;
if (max[1] > scaled_width() - 1)
max[1] = scaled_width() - 1;
bb[0] = min;
bb[1] = max;
}
void get_view_local_bb_unscaled(point volume_min, point volume_max, point bb[2]) const {
get_view_local_bb(volume_min, volume_max, bb, 0);
}
void get_view_local_bb_scaled(point volume_min, point volume_max, point bb[2]) const {
get_view_local_bb(volume_min, volume_max, bb, 1);
}
void get_view_local_bb_scaled(const space::traverse &st, point bb[2]) const {
get_view_local_bb_scaled(st.get_min(), st.get_max(), bb);
}
void get_view_local_bb_unscaled(const space::traverse &t, point bb[2]) const {
get_view_local_bb_unscaled(t.get_min(), t.get_max(), bb);
}
/*
* Get the in-bounds centroid for a subspace, if one exists.
*/
point centroid(point volume_min, point volume_max) const {
point bb[2];
get_view_local_bb_unscaled(volume_min, volume_max, bb);
point min = bb[0];
point max = bb[1];
for (int d = 0; d < 2; d++)
if (min[d] > max[d])
return point::undefined();
if (min[2] >= 0)
return point::undefined();
if (max[2] > 0)
max[2] = 0;
return (max + min) / 2;
}
point centroid(const space::traverse &t) {
return centroid(t.get_min(), t.get_max());
}
/*
* Get the local space origin in world space.
*/
point origin() {
return cw(point(0, 0, 0));
}
};
#endif

51
d3/render.h Normal file
View File

@@ -0,0 +1,51 @@
// Copyright 2004 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
/*
* render.h: A superclass for all rendering classes.
*/
#ifndef __3drender_h__
#define __3drender_h__
#include "point.h"
/*
* Class render accepts descriptions of scenes. Subclasses may produce various
* types of output.
*/
class render {
public:
/*
* Describe a sphere at position p with radius r. Non-negative 'frame'
* less than the total number of frames indicates that the position is
* specified in the local space of that frame number. Otherwise, the
* position is specified in world coordinates.
*/
virtual void describe(int frame, point p, ale_real r) = 0;
virtual ~render() {
}
};
#endif

81
d3/scene.cc Normal file
View File

@@ -0,0 +1,81 @@
// Copyright 2003, 2004, 2005 David Hilvert <dhilvert@gmail.com>,
// <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
#include "scene.h"
/*
* See scene.h for details on these variables.
*/
scene::lod_images *scene::al;
ale_pos scene::front_clip = 0;
int scene::input_decimation_lower = 0;
int scene::output_decimation_preferred = 0;
int scene::primary_decimation_upper = 0;
int scene::output_clip = 0;
ale_pos scene::rear_clip = 0;
const char *scene::load_model_name = NULL;
const char *scene::save_model_name = NULL;
const ale_real scene::nearness = 0.01;
scene::spatial_info_map_t scene::spatial_info_map;
// scene::spatial_info *scene::tracked_space = NULL;
unsigned long scene::total_ambiguity = 0;
unsigned long scene::total_pixels = 0;
unsigned long scene::total_divisions = 0;
unsigned long scene::total_tsteps = 0;
double scene::occ_att = 0.50;
int scene::normalize_weights = 1;
int scene::use_filter = 1;
const char *scene::d3chain_type = "";
ale_real scene::falloff_exponent = 1;
double scene::tc_multiplier = 0;
unsigned int scene::ou_iterations = 10;
unsigned int scene::pairwise_ambiguity = 3;
const char *scene::pairwise_comparisons = "auto";
int scene::d3px_count;
double *scene::d3px_parameters;
double scene::encounter_threshold = 0;
double scene::depth_median_radius = 0;
double scene::diff_median_radius = 0;
int scene::subspace_traverse = 0;
/*
* Precision discriminator
*
* For some reason, colors that should be identical are calculated differently
* along different computational pathways, either due to a compiler idiosyncrasy,
* or due to an as-of-yet undiscovered bug in the code. We use the following
* constant in an effort to winnow out these discrepancies, which can sometimes
* cause cycles in the adjustment preference function.
*/
#define DISCRIMINATOR 1e-5
/*
* Functions.
*/

4933
d3/scene.h Normal file

File diff suppressed because it is too large Load Diff

28
d3/space.cc Normal file
View File

@@ -0,0 +1,28 @@
// Copyright 2003, 2004, 2005 David Hilvert <dhilvert@gmail.com>,
// <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
#include "space.h"
/*
* See space.h for details on these variables.
*/
space::node *space::root_node = NULL;

333
d3/space.h Normal file
View File

@@ -0,0 +1,333 @@
// Copyright 2003, 2004, 2005 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
/*
* d3/space.h: Representation of 3D space.
*/
#ifndef __space_h__
#define __space_h__
#include "point.h"
class space {
public:
/*
* Structure to hold a subdivisible region of space.
*/
struct node {
struct node *positive;
struct node *negative;
node() {
positive = NULL;
negative = NULL;
}
};
private:
/*
* Space root pointer
*/
static node *root_node;
public:
static void init_root() {
root_node = new node;
}
/*
* Space traversal and navigation class.
*/
class traverse {
node *current;
point bounds[2];
public:
static int get_next_split(point min, point max) {
assert(min[0] < max[0]);
assert(min[1] < max[1]);
assert(min[2] < max[2]);
/*
* Double-infinite case.
*/
for (int d = 0; d < 3; d++)
if (isinf(max[d]) && isinf(min[d]))
return d;
/*
* Finite or single-infinite case
*/
if (max[0] - min[0] >= max[1] - min[1]
&& (max[0] >= max[1] || !isinf(min[1]))
&& (min[0] <= min[1] || !isinf(max[1]))
&& max[0] - min[0] >= max[2] - min[2]
&& (max[0] >= max[2] || !isinf(min[2]))
&& (min[0] <= min[2] || !isinf(max[2])))
return 0;
if (max[1] - min[1] > max[2] - min[2]
&& (max[1] >= max[2] || !isinf(min[2]))
&& (min[1] <= min[2] || !isinf(max[2])))
return 1;
return 2;
}
static ale_pos split_coordinate(int d, point min, point max) {
if (isinf(max[d]) && isinf(min[d]))
return 0;
if (isinf(max[d]))
return tan((atan(min[d]) + M_PI/2) / 2);
if (isinf(min[d]))
return tan((atan(max[d]) - M_PI/2) / 2);
return (min[d] + max[d]) / 2;
}
static int get_next_cells(int d, point min, point max, point cells[2][2]) {
cells[0][0] = min;
cells[0][1] = max;
cells[1][0] = min;
cells[1][1] = max;
ale_pos split_point = split_coordinate(d, min, max);
if (split_point == min[d]
|| split_point == max[d]
|| !finite(split_point))
return 0;
cells[0][1][d] = split_point;
cells[1][0][d] = split_point;
return 1;
}
int get_next_split() {
return get_next_split(bounds[0], bounds[1]);
}
ale_pos split_coordinate(int d) {
return split_coordinate(d, bounds[0], bounds[1]);
}
ale_pos split_coordinate() {
int next_split = get_next_split();
return split_coordinate(next_split);
}
static traverse root() {
traverse result;
result.current = root_node;
result.bounds[0] = point::neginf();
result.bounds[1] = point::posinf();
assert(result.current);
return result;
}
int precision_wall() {
int next_split = get_next_split();
ale_pos split_point = split_coordinate(next_split);
point &min = bounds[0];
point &max = bounds[1];
assert(split_point <= max[next_split]);
assert(split_point >= min[next_split]);
if (split_point == min[next_split] || split_point == max[next_split])
return 1;
return 0;
}
traverse positive() {
assert(current);
int next_split = get_next_split();
if (current->positive == NULL) {
current->positive = new node;
}
traverse result;
result.current = current->positive;
result.bounds[0] = bounds[0];
result.bounds[1] = bounds[1];
result.bounds[0][next_split] = split_coordinate(next_split);
assert(result.current);
return result;
}
traverse negative() {
assert(current);
int next_split = get_next_split();
if (current->negative == NULL) {
current->negative = new node;
}
traverse result;
result.current = current->negative;
result.bounds[0] = bounds[0];
result.bounds[1] = bounds[1];
result.bounds[1][next_split] = split_coordinate(next_split);
assert(result.current);
return result;
}
point get_min() const {
return bounds[0];
}
point get_max() const {
return bounds[1];
}
const point *get_bounds() const {
return bounds;
}
point get_centroid() const {
return (bounds[0] + bounds[1]) / 2;
}
int includes(point p) {
for (int d = 0; d < 3; d++) {
if (p[d] > bounds[1][d])
return 0;
if (p[d] < bounds[0][d])
return 0;
if (isnan(p[d]))
return 0;
}
return 1;
}
node *get_node() {
assert(current);
return current;
}
};
/*
* Class to iterate through subspaces based on proximity to a camera.
*/
class iterate {
std::stack<traverse> node_stack;
point camera_origin;
public:
iterate(point co, traverse top = traverse::root()) {
camera_origin = co;
node_stack.push(top);
}
int next() {
if (node_stack.empty())
return 0;
traverse st = node_stack.top();
int d = st.get_next_split();
ale_pos split_coordinate = st.split_coordinate();
node *n = st.get_node()->negative;
node *p = st.get_node()->positive;
if (camera_origin[d] > split_coordinate) {
if (n) {
node_stack.top() = st.negative();
if (p)
node_stack.push(st.positive());
} else {
if (p)
node_stack.top() = st.positive();
else
node_stack.pop();
}
} else {
if (p) {
node_stack.top() = st.positive();
if (n)
node_stack.push(st.negative());
} else {
if (n)
node_stack.top() = st.negative();
else
node_stack.pop();
}
}
return (!node_stack.empty());
}
iterate cleave() {
assert (!node_stack.empty());
iterate result(camera_origin, node_stack.top());
node_stack.pop();
return result;
}
int done() {
return node_stack.empty();
}
traverse get() {
assert (!node_stack.empty());
return node_stack.top();
}
};
};
#endif

28
d3/tfile.cc Normal file
View File

@@ -0,0 +1,28 @@
// Copyright 2002 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
#include "tfile.h"
/*
* See tfile.h for details on these variables.
*/
int tfile_input_version = 0;
int tfile_output_version = 0;

584
d3/tfile.h Normal file
View File

@@ -0,0 +1,584 @@
// Copyright 2002, 2003, 2005 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
/*
* d3/tfile.h: Read and write 3D transformation data files.
*/
/*
* This version of ALE reads and writes version 0 3D transformation data files.
*/
#ifndef __d3tfile_h__
#define __d3tfile_h__
#include "pt.h"
#define D3_TFILE_VERSION 0
#define D3_TFILE_VERSION_MAX 0
extern int tfile_input_version;
extern int tfile_output_version;
/*
* Structure to describe a transformation data file to load data from.
*/
struct tload_t {
const char *filename;
FILE *file;
};
/*
* Structure to describe a transformation data file to write data to.
*/
struct tsave_t {
const char *filename;
const char *target;
const char *orig;
FILE *file;
};
/*
* Create a new tload_t transformation data file structure, used for
* reading data from transformation data files.
*/
static inline struct tload_t *tload_new(const char *filename) {
FILE *file = fopen (filename, "r");
struct tload_t *result = NULL;
if (!file) {
fprintf(stderr, "tload: Error: could not open transformation data file '%s'.", filename);
exit(1);
}
result = (struct tload_t *)
malloc(sizeof(struct tload_t));
result->filename = filename;
result->file = file;
return result;
}
/*
* Load the first transformation from a transformation data file associated with
* transformation data file structure T, or return the default transformation
* if no transformation is available.
*
* T is a pointer to the tload_t transformation data file structure.
*
* DEFAULT_TRANSFORM is the default transformation result.
*
* IS_DEFAULT is used to signal a non-default transformation result.
*/
static inline pt tload_first(struct tload_t *t,
pt default_transform, int *is_default) {
pt result = default_transform;
*is_default = 1;
/*
* If there is no file, return the default.
*/
if (t == NULL)
return result;
/*
* Search through the initial part of the file to determine
* its version.
*/
/*
* Skip comments
*/
int first_character;
first_character = fgetc(t->file);
while (first_character == ' '
|| first_character == 0xa
|| first_character == 0xd
|| first_character == '\t'
|| first_character == '#') {
ungetc(first_character, t->file);
char line[1024];
fgets(line, 1024, t->file);
if (strlen(line) >= 1023) {
fprintf(stderr,
"\n3d-trans-load: Error: line too long in input file\n");
exit(1);
}
first_character = fgetc(t->file);
}
if (first_character != EOF)
ungetc(first_character, t->file);
if (first_character != 'W') {
fprintf(stderr, "\n3d-trans-load: First command must be a version number.\n");
exit(1);
}
/*
* Obtain version from version command string.
*/
char line[1024];
fgets(line, 1024, t->file);
if (strlen(line) >= 1023) {
fprintf(stderr,
"\n3d-trans-load: Error: line too long in input file\n");
exit(1);
}
int count = sscanf(line, "W %d", &tfile_input_version);
if (count < 1) {
fprintf(stderr, "Error in 3d transformation "
"file version command.\n");
exit(1);
} else if (tfile_input_version > D3_TFILE_VERSION_MAX) {
fprintf(stderr, "Unsupported 3D transformation "
"file version %d\n",
tfile_input_version);
exit(1);
}
/*
* Read each line of the file until we find a transformation
* or EOF.
*/
while (!feof(t->file)) {
char line[1024];
fgets(line, 1024, t->file);
if (feof(t->file))
return result;
if (strlen(line) >= 1023) {
fprintf(stderr,
"\ntrans-load: Error: line too long in input file\n");
exit(1);
}
const double rtod_multiplier = 180 / M_PI;
switch (line[0]) {
case ' ':
case 0xa:
case 0xd:
case '\t':
case '#':
/* Comment or whitespace */
break;
case 'D':
case 'd':
/* Default transformation */
return result;
case 'V':
case 'v':
unsigned int count;
double view_angle;
count = sscanf(line, "V %lf", &view_angle);
if (count < 1) {
fprintf(stderr, "\n3d-trans-load: Error: "
"Malformed 'V' command.\n");
exit(1);
}
result.view_angle(view_angle / rtod_multiplier);
break;
case 'E':
case 'e':
/* Euclidean transformation data */
*is_default = 0;
{
double width, height;
double values[6] = {0, 0, -1, 0, 0, 0};
int count;
count = sscanf(line, "E %lf%lf%lf%lf%lf%lf%lf%lf",
&width, &height,
&values[1], &values[0], &values[2],
&values[4], &values[3], &values[5]);
if (count < 8)
fprintf(stderr, "\n3d-trans-load: warning: "
"Missing args for 'E'\n");
if (width != result.scaled_width()
|| height != result.scaled_height()) {
fprintf(stderr, "\n3d-trans-load: Error: "
"Scaled image width and/or height mismatch.");
}
for (int i = 3; i < 6; i++) {
values [i] /= rtod_multiplier;
}
result.e().set(values);
return result;
}
break;
default:
fprintf(stderr,
"\ntrans-load: Error in tload_first: unrecognized command '%s'\n",
line);
exit(1);
}
}
/*
* EOF reached: return default transformation.
*/
return result;
}
/*
* Load the next transformation from a transformation data file associated with
* transformation data file structure T, or return the default transformation
* if no transformation is available.
*
* T is a pointer to the tload_t transformation data file structure.
*
* IS_P is nonzero if a projective transformation is expected.
*
* DEFAULT_TRANSFORM is the default transformation result.
*
* IS_DEFAULT is used to signal a non-default transformation result.
*/
static inline pt tload_next(struct tload_t *t,
pt default_transform, int *is_default) {
pt result = default_transform;
*is_default = 1;
/*
* Read each line of the file until we find a transformation.
*/
while (t && !feof(t->file)) {
char line[1024];
fgets(line, 1024, t->file);
if (feof(t->file))
return result;
if (strlen(line) >= 1023) {
fprintf(stderr,
"\ntrans-load: warning: line too long in input file\n");
}
const double rtod_multiplier = 180 / M_PI;
switch (line[0]) {
case ' ':
case 0xa:
case 0xd:
case '\t':
case '#':
/* Comment or whitespace */
break;
case 'D':
case 'd':
/* Default transformation */
return result;
case 'V':
case 'v':
unsigned int count;
double view_angle;
count = sscanf(line, "V %lf", &view_angle);
if (count < 1) {
fprintf(stderr, "\n3d-trans-load: Error: "
"Malformed 'V' command.\n");
exit(1);
}
result.view_angle(view_angle / rtod_multiplier);
break;
case 'E':
case 'e':
/* Euclidean transformation data */
*is_default = 0;
{
double width, height;
double values[6] = {0, 0, -1, 0, 0, 0};
int count;
count = sscanf(line, "E %lf%lf%lf%lf%lf%lf%lf%lf",
&width, &height,
&values[1], &values[0], &values[2],
&values[4], &values[3], &values[5]);
if (count < 8)
fprintf(stderr, "\n3d-trans-load: warning: "
"Missing args for 'E'\n");
if (width != result.scaled_width()
|| height != result.scaled_height()) {
fprintf(stderr, "\n3d-trans-load: Error: "
"Scaled image width and/or height mismatch.");
}
for (int i = 3; i < 6; i++) {
values [i] /= rtod_multiplier;
}
result.e().set(values);
return result;
}
break;
default:
fprintf(stderr,
"\ntrans-load: Error in tload_next: unrecognized command '%s'\n",
line);
exit(1);
}
}
return result;
}
/*
* Create a new tsave_t transformation data file structure, used for
* writing data to transformation data files.
*/
static inline struct tsave_t *tsave_new(const char *filename) {
FILE *file = fopen (filename, "w");
struct tsave_t *result = NULL;
if (!file) {
fprintf(stderr, "tsave: Error: could not open transformation data file '%s'.", filename);
exit(1);
}
result = (struct tsave_t *)
malloc(sizeof(struct tsave_t));
result->filename = filename;
result->file = file;
result->orig = "unknown";
result->target = "unknown";
fprintf(file, "# created by ALE 3D transformation file handler version %d\n",
D3_TFILE_VERSION);
fclose(file);
return result;
}
/*
* Save the first transformation to a transformation data file associated with
* transformation data file structure T, or do nothing if T is NULL. This
* function also establishes the output file version.
*
* OFFSET is the transformation to be saved.
*
* IS_PROJECTIVE indicates whether to write a projective transformation.
*
*/
static inline void tsave_first(struct tsave_t *t, pt offset) {
if (t == NULL)
return;
t->file = fopen(t->filename, "a");
tfile_output_version = 0;
fprintf(t->file, "# producing 3D transformation file syntax version %d\n", tfile_output_version);
fprintf(t->file, "W %d\n", tfile_output_version);
// fprintf(t->file, "# Comment: Target output file is %s\n", t->target);
// fprintf(t->file, "# Comment: Original frame is %s\n", t->orig);
const double rtod_multiplier = 180 / M_PI;
fprintf(t->file, "V %lf\n", (double) offset.view_angle() * rtod_multiplier);
fprintf(t->file, "E ");
fprintf(t->file, "%f %f ", (double) offset.scaled_width(), (double) offset.scaled_height());
fprintf(t->file, "%f ", (double) offset.e().get_translation(1));
fprintf(t->file, "%f ", (double) offset.e().get_translation(0));
fprintf(t->file, "%f ", (double) offset.e().get_translation(2));
fprintf(t->file, "%f ", (double) offset.e().get_rotation(1) * rtod_multiplier);
fprintf(t->file, "%f ", (double) offset.e().get_rotation(0) * rtod_multiplier);
fprintf(t->file, "%f ", (double) offset.e().get_rotation(2) * rtod_multiplier);
fprintf(t->file, "\n");
fclose(t->file);
}
/*
* Save the next transformation to a transformation data file associated with
* transformation data file structure T, or do nothing if T is NULL.
*
* OFFSET is the transformation to be saved.
*
* IS_PROJECTIVE indicates whether to write a projective transformation.
*
*/
static inline void tsave_next(struct tsave_t *t, pt offset) {
if (t == NULL)
return;
t->file = fopen(t->filename, "a");
const double rtod_multiplier = 180 / M_PI;
fprintf(t->file, "V %lf\n", (double) offset.view_angle() * rtod_multiplier);
fprintf(t->file, "E ");
fprintf(t->file, "%f %f ", (double) offset.scaled_width(), (double) offset.scaled_height());
fprintf(t->file, "%f ", (double) offset.e().get_translation(1));
fprintf(t->file, "%f ", (double) offset.e().get_translation(0));
fprintf(t->file, "%f ", (double) offset.e().get_translation(2));
fprintf(t->file, "%f ", (double) offset.e().get_rotation(1) * rtod_multiplier);
fprintf(t->file, "%f ", (double) offset.e().get_rotation(0) * rtod_multiplier);
fprintf(t->file, "%f ", (double) offset.e().get_rotation(2) * rtod_multiplier);
fprintf(t->file, "\n");
fclose(t->file);
}
/*
* Write information to a transformation file indicating the target output
* file.
*/
static inline void tsave_target(struct tsave_t *t, const char *filename) {
if (t == NULL)
return;
t->target = filename;
if (t != NULL) {
t->file = fopen(t->filename, "a");
fclose(t->file);
}
}
/*
* Write information to a transformation data file indicating the filename
* of the original frame (i.e. the first frame in the sequence of input
* frames).
*/
static inline void tsave_orig(struct tsave_t *t, const char *filename) {
if (t == NULL)
return;
t->orig = filename;
}
/*
* Write information to a transformation data file indicating the filename
* of a supplemental frame (i.e. a frame in the sequence of input frames
* that is not the first frame).
*/
static inline void tsave_info(struct tsave_t *t, const char *filename) {
if (t != NULL) {
t->file = fopen(t->filename, "a");
fprintf(t->file, "# Comment: Supplemental frame %s\n", filename);
fclose(t->file);
}
}
/*
* Write information to a transformation data file indicating the tonal
* registration multiplier.
*/
static inline void tsave_trm(struct tsave_t *t, ale_real r, ale_real g, ale_real b) {
if (t != NULL) {
t->file = fopen(t->filename, "a");
fprintf(t->file, "# Comment: Exposure [r=%f g=%f b=%f]\n", (double) r, (double) g, (double) b);
fclose(t->file);
}
}
/*
* Write information to a transformation data file indicating the average
* pixel magnitude.
*/
static inline void tsave_apm(struct tsave_t *t, ale_real r, ale_real g, ale_real b) {
if (t != NULL) {
t->file = fopen(t->filename, "a");
fprintf(t->file, "# Comment: Avg magnitude [r=%f g=%f b=%f]\n", (double) r, (double) g, (double) b);
fclose(t->file);
}
}
/*
* Destroy a tload_t transformation data file structure.
*/
static inline void tload_delete(struct tload_t *victim) {
if (victim)
fclose(victim->file);
free(victim);
}
/*
* Destroy a tsave_t transformation data file structure.
*/
static inline void tsave_delete(struct tsave_t *victim) {
free(victim);
}
#endif

58
d3/view.h Normal file
View File

@@ -0,0 +1,58 @@
// Copyright 2004 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* 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
*/
/*
* view.h: A render subclass implementing views
*/
#ifndef __view_h__
#define __view_h__
#include "render.h"
class view : public render {
/*
* Z-buffer rendering data
*
* XXX: there is no immediately obvious reason for depth to use RGB
* triples.
*/
d2::image *result;
d2::image *depth;
public:
/*
* Describe a sphere at position p with radius r. Non-negative 'frame'
* less than the total number of frames indicates that the position is
* specified in the local space of that frame number. Otherwise, the
* position is specified in world coordinates.
*/
virtual void describe(int frame, point p, ale_real r) {
}
d2::image *output() {
}
};
#endif