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

95
d2/align.cc Normal file
View File

@@ -0,0 +1,95 @@
// 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 "align.h"
/*
* See align.h for details on these variables.
*/
int align::_exp_register = 1;
ale_pos align::scale_factor;
transformation align::orig_t = transformation::eu_identity();
int align::_keep = 0;
transformation *align::kept_t = NULL;
int *align::kept_ok = NULL;
tload_t *align::tload = NULL;
tsave_t *align::tsave = NULL;
render *align::reference = NULL;
filter::scaled_filter *align::interpolant = NULL;
const image *align::reference_image = NULL;
const image *align::reference_defined = NULL;
const image *align::weight_map = NULL;
image *align::alignment_weights = NULL;
const char *align::wmx_file = NULL;
const char *align::wmx_exec = NULL;
const char *align::wmx_defs = NULL;
const char *align::fw_output = NULL;
double align::horiz_freq_cut = 0;
double align::vert_freq_cut = 0;
double align::avg_freq_cut = 0;
transformation align::latest_t = transformation::eu_identity();
int align::latest_ok;
int align::latest = -1;
int align::alignment_class = 1;
int align::default_initial_alignment_type = 1;
int align::perturb_type = 0;
int align::is_fail_default = 0;
int align::channel_alignment_type = 2;
ale_real align::metric_exponent = 2;
float align::match_threshold = -1;
/*
* Upper/lower bounds
*/
ale_pos align::perturb_lower = 0.125;
int align::perturb_lower_percent = 0;
ale_pos align::perturb_upper = 14;
int align::perturb_upper_percent = 1;
int align::lod_preferred = -3;
int align::min_dimension = 10;
ale_pos align::rot_max = 32.0;
ale_pos align::bda_mult = 2;
ale_pos align::bda_rate = 8;
ale_accum align::match_sum = 0;
int align::match_count = 0;
ale_pos align::_mc = 30;
int align::certainty_weights = 0;
int align::_gs = 6;
ale_accum align::_gs_mo = 67;
int align::gs_mo_percent = 1;
ale_real align::_ma_cert = 0.01;
exclusion *align::ax_parameters = NULL;
int align::ax_count = 0;
const point **align::cp_array = NULL;
unsigned int align::cp_count = 0;

3781
d2/align.h Normal file

File diff suppressed because it is too large Load Diff

43
d2/exclusion.h Normal file
View File

@@ -0,0 +1,43 @@
// 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
*/
#ifndef __exclusion_h__
#define __exclusion_h__
/*
* Header file for exclusion regions.
*/
/*
* Exclusion data structure.
*
* XXX: For now, we're using floating point boundary values;
* this might be somewhat inefficient in cases where the values
* will always be integer.
*/
struct exclusion {
enum {RENDER, FRAME};
char type;
ale_pos x[6];
};
#endif

24
d2/exposure/exposure.cc Normal file
View File

@@ -0,0 +1,24 @@
// 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
*/
#include "exposure.h"
ale_real exposure::confidence_exponent = (ale_real) 1.0;
ale_real exposure::_gain_reference = 1;

258
d2/exposure/exposure.h Normal file
View File

@@ -0,0 +1,258 @@
// 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
*/
/*
* exposure.h: A superclass for all exposure classes.
*/
#ifndef __exposure_h__
#define __exposure_h__
#include "../pixel.h"
/*
* This class models a non-linear response function. More information can be
* found here:
*
* http://wearcam.org/comparametrics.pdf
*/
class exposure {
private:
static ale_real confidence_exponent;
pixel _multiplier;
ale_real _gain_multiplier;
static ale_real _gain_reference;
ale_real _black_level;
public:
/*
* confidence/uniform static mutators
*/
static void set_confidence(ale_real exponent) {
confidence_exponent = exponent;
}
/*
* confidence accessor
*/
static ale_real get_confidence() {
return confidence_exponent;
}
/*
* Event listener interface
*/
class listener {
friend class exposure;
private:
listener *next;
const char *name;
const exposure *target;
public:
virtual void trigger(pixel multiplier) = 0;
listener () {
next = NULL;
target = NULL;
}
virtual ~listener() {
if (target) {
target->remove_listener(this);
}
}
};
private:
mutable listener *listener_head;
public:
void add_listener(listener *l, const char *name) const {
/*
* This is a metafunction, so we consider it
* to leave the object constant.
*/
assert (l->next == NULL);
assert (l->target == NULL);
l->next = listener_head;
l->target = this;
l->name = name;
listener_head = l;
}
void remove_listener(listener *l) const {
assert (listener_head != NULL);
if (listener_head == l) {
listener_head = listener_head->next;
} else {
assert (listener_head->next != NULL);
listener *a = listener_head;
listener *b = listener_head->next;
while (b != l) {
assert (b->next != NULL);
a = b;
b = b->next;
}
a->next = b->next;
}
l->target = NULL;
}
void set_multiplier(pixel _multiplier) {
listener *cl = listener_head;
while(cl != NULL) {
cl->trigger(_multiplier / this->_multiplier);
cl = cl->next;
}
this->_multiplier = _multiplier;
}
void set_gain_multiplier(ale_real g) {
_gain_multiplier = g;
}
ale_real get_gain_multiplier() {
return _gain_multiplier;
}
void set_black_level(ale_real b) {
_black_level = b;
}
ale_real get_black_level() {
return _black_level;
}
static void set_gain_reference(ale_real r) {
_gain_reference = r;
}
static ale_real get_gain_reference() {
return _gain_reference;
}
pixel get_multiplier() const {
return _multiplier;
}
virtual ale_real confidence(unsigned int k, ale_real input,
ale_real confidence_floor = ale_real_confidence_floor) const {
ale_real _0 = (ale_real) 0;
ale_real _4 = (ale_real) 4;
ale_real _2 = (ale_real) 2;
ale_real _1 = (ale_real) 1;
if (confidence_exponent == _0)
return 1;
ale_real input_scaled = input / _multiplier[k];
ale_real unexponentiated = _1 - _4 * ((_1 / _2) - input_scaled)
* ((_1 / _2) - input_scaled);
// ale_real unexponentiated = _4 * ((_1 / _4) - (ale_real) ((_1 / _2) - input_scaled)
// * ((_1 / _2) - input_scaled));
// ale_real unexponentiated = 4 * input_scaled * (0.25 - pow(0.5 - input_scaled, 2));
ale_real exponentiated;
if (confidence_exponent != _1) {
if (unexponentiated < _0)
return confidence_floor;
exponentiated = pow(unexponentiated, confidence_exponent);
} else {
exponentiated = unexponentiated;
}
if (exponentiated < confidence_floor || !finite(exponentiated))
return confidence_floor;
return exponentiated;
}
/*
* This is a very hackish confidence function. It's zero at the
* extremes of camera response and maximal at the center.
*/
virtual pixel confidence(pixel input,
ale_real confidence_floor = ale_real_confidence_floor) const {
if (confidence_exponent != 0) {
return pixel(confidence(0, input[0], confidence_floor),
confidence(1, input[1], confidence_floor),
confidence(2, input[2], confidence_floor));
} else {
return pixel(1, 1, 1);
}
}
/*
* Confidence that the real value is lower or higher than the given
* value.
*
* XXX: This function now applies the one-sided condition only to
* responses greater than 50%. Should it be called
* 'upper_one_sided_confidence' instead?
*/
virtual pixel one_sided_confidence(pixel input, pixel sign) const {
if (confidence_exponent != 0) {
pixel result = confidence(input);
for (unsigned int k = 0; k < 3; k++) {
if (sign[k] > 0 && input[k] / _multiplier[k] > 1 / (ale_real) 2)
result[k] = 1;
}
return result;
} else {
return pixel(1, 1, 1);
}
}
virtual pixel linearize(pixel input) const = 0;
virtual pixel unlinearize(pixel input) const = 0;
exposure() {
listener_head = NULL;
_multiplier = pixel(1, 1, 1);
_gain_multiplier = 1;
_black_level = 0;
}
virtual ~exposure() {
}
};
#endif

View File

@@ -0,0 +1,46 @@
// 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
*/
/*
* exposure_boolean.h: Boolean exposure properties.
*/
#ifndef __exposure_boolean_h__
#define __exposure_boolean_h__
/*
* Boolean exposure.
*/
class exposure_boolean : public exposure {
public:
pixel linearize(pixel input) const {
for (int k = 0; k < 3; k++)
input[k] = (input[k] == 0) ? 0 : 1;
return input;
}
pixel unlinearize(pixel input) const {
for (int k = 0; k < 3; k++)
input[k] = (input[k] == 0) ? 0 : 1;
return input;
}
};
#endif

View File

@@ -0,0 +1,160 @@
// 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
*/
/*
* exposure_default.h: Default exposure properties.
*/
#ifndef __exposure_default_h__
#define __exposure_default_h__
/*
* The default exposure is modeled after the simple power transfer function
* described in
*
* http://netpbm.sourceforge.net/doc/pnmgamma.html
*
* Note: optimizations in d2/image_rw.h depend on the details of this function.
*/
class exposure_default : public exposure {
public:
pixel linearize(pixel input) const {
#if 0
/*
* Calling pow() may be expensive on some platforms (e.g.,
* those lacking hardware support for floating point).
*/
return ppow(input, 1/0.45) * get_multiplier();
#else
const int table_size = 1024;
const int table_bits = 10;
const int interp_bits = 6;
static int table_is_built = 0;
static ale_real table[table_size];
pixel result;
if (!table_is_built) {
for (int i = 0; i < table_size; i++) {
table[i] = pow((float) i / (float) (table_size - 1), 1/0.45);
}
table_is_built = 1;
}
for (int k = 0; k < 3; k++) {
/*
* Clamp.
*/
if (input[k] >= 1) {
result[k] = 1;
continue;
} else if (input[k] <= 0) {
result[k] = 0;
continue;
} else if (isnan(input[k])) {
result[k] = input[k];
continue;
}
int index1 = ale_real_to_int(input[k], 65535);
int index2 = index1 >> (16 - table_bits);
int index3 = (index1 >> (16 - table_bits - interp_bits))
& ((1 << interp_bits) - 1);
if (index2 >= table_size - 1) {
result[k] = 1;
continue;
}
ale_real frac = ale_real_from_int((index3 << (16 - interp_bits)), 65535);
result[k] = (1 - frac) * table[index2] + frac * table[index2 + 1];
}
return result * get_multiplier();
#endif
}
pixel unlinearize(pixel input) const {
#if 0
/*
* Calling pow() may be expensive on some platforms (e.g.,
* those lacking hardware support for floating point).
*/
return ppow(input / get_multiplier(), 0.45);
#else
input /= get_multiplier();
const int table_size = 1024;
const int table_bits = 10;
const int interp_bits = 6;
static int table_is_built = 0;
static ale_real table[table_size];
pixel result;
if (!table_is_built) {
for (int i = 0; i < table_size; i++) {
table[i] = pow((float) i / (float) (table_size - 1), 0.45);
}
table_is_built = 1;
}
for (int k = 0; k < 3; k++) {
/*
* Clamp.
*/
if (input[k] >= 1) {
result[k] = 1;
continue;
} else if (input[k] <= 0) {
result[k] = 0;
continue;
} else if (isnan(input[k])) {
result[k] = input[k];
continue;
}
int index1 = ale_real_to_int(input[k], 65535);
int index2 = index1 >> (16 - table_bits);
int index3 = (index1 >> (16 - table_bits - interp_bits))
& ((1 << interp_bits) - 1);
if (index2 >= table_size - 1) {
result[k] = 1;
continue;
}
ale_real frac = ale_real_from_int((index3 << (16 - interp_bits)), 65535);
result[k] = (1 - frac) * table[index2] + frac * table[index2 + 1];
}
return result;
#endif
}
};
#endif

View File

@@ -0,0 +1,42 @@
// 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
*/
/*
* exposure_linear.h: Linear exposure properties.
*/
#ifndef __exposure_linear_h__
#define __exposure_linear_h__
/*
* Linear exposure.
*/
class exposure_linear : public exposure {
public:
pixel linearize(pixel input) const {
return input * get_multiplier();
}
pixel unlinearize(pixel input) const {
return input / get_multiplier();
}
};
#endif

51
d2/filter.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
*/
#ifndef __filternamespace_h__
#define __filternamespace_h__
/*
* Include files from the current namespace
*/
#include "point.h"
#include "transformation.h"
#include "render.h"
#include "image.h"
#include "image_ale_real.h"
#include "image_bayer_ale_real.h"
/*
* Establish a new namespace for all filtering classes.
*/
namespace filter {
#include "filter/filter.h"
#include "filter/sinc.h"
#include "filter/lanczos.h"
#include "filter/triangle.h"
#include "filter/box.h"
#include "filter/gauss.h"
#include "filter/zero.h"
#include "filter/mult.h"
#include "filter/scaled_filter.h"
#include "filter/ssfe.h"
};
#endif

74
d2/filter/box.h Normal file
View File

@@ -0,0 +1,74 @@
// 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
*/
#ifndef __d2_filter_box_h__
#define __d2_filter_box_h__
/*
* A box filter class.
*/
class box : public filter {
private:
ale_pos half_width;
/*
* Box filter.
*/
ale_real _box(ale_pos p) const {
if (fabs(p) > half_width)
return 0;
return 1;
}
ale_real _box(point p) const {
return _box(p[0]) * _box(p[1]);
}
public:
/*
* Size of filter support, in number of half-cycles to each side of the
* filter center.
*/
ale_pos support() const {
return half_width;
}
/*
* Response of filter at point p
*/
virtual ale_real response(point p) const {
return _box(p);
}
virtual int equals(const filter *f) const {
if (typeid(*f) == typeid(*this))
return ((box *)f)->half_width == half_width;
return 0;
}
box(ale_pos half_width) {
this->half_width = half_width;
}
};
#endif

47
d2/filter/filter.h Normal file
View File

@@ -0,0 +1,47 @@
// 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
*/
#ifndef __filterclass_h__
#define __filterclass_h__
/*
* A superclass for all filtering classes.
*/
class filter {
public:
/*
* Size of filter support, in number of half-cycles to each side of the
* filter center.
*/
virtual ale_pos support() const = 0;
/*
* Response of filter at point p
*/
virtual ale_real response(point p) const = 0;
virtual int equals(const filter *f) const = 0;
virtual ~filter(){
}
};
#endif

92
d2/filter/gauss.h Normal file
View File

@@ -0,0 +1,92 @@
// code by HJ Hornbeck, based on code copyright 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
*/
#ifndef __d2_filter_gauss_h__
#define __d2_filter_gauss_h__
/*
* A Gaussian filter.
*/
class gauss : public filter {
private:
ale_pos sigma;
ale_pos diameter; /** measured in standard deviations **/
/*
* The heavy-lifting function:
*/
ale_real _calc(ale_pos p) const {
/** catch the trivial cases **/
if ( p == 0 ) return 1;
/** if ( p > (sigma*diameter) ) return 0;
^^ unnecessary, gauss is well-behaved
outside its radius **/
/** calculate the rest **/
ale_pos p2 = p * p;
ale_pos sigma2 = sigma * sigma;
return exp( -(p2/sigma2) );
}
/** extend the heavy-lifting function to handle a coordinate **/
ale_real _calc(point p) const {
return _calc( sqrt( (p[0]*p[0]) + (p[1]*p[1]) ) );
/** doesn't work as well:
return _calc( p[0] ) * _calc( p[1] ); **/
}
public:
/*
* Size of filter support, or how big a radius does this filter
* touch?
*/
ale_pos support() const {
return sigma*diameter;
}
/*
* Response of filter at point p
*/
virtual ale_real response(point p) const {
return _calc(p);
}
/** only compare standard deviations, since this version fixes
the diameter across all instances **/
virtual int equals(const filter *f) const {
if (typeid(*f) == typeid(*this))
return ((gauss *)f)->sigma == sigma;
return 0;
}
gauss(ale_pos sigma) {
this->sigma = sigma;
this->diameter = 2; /** fixed, standard for imaging **/
}
};
#endif

81
d2/filter/lanczos.h Normal file
View File

@@ -0,0 +1,81 @@
// 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
*/
#ifndef __lanczos_h__
#define __lanczos_h__
/*
* A lanczos filter class.
*
* Notes on Lanczos and Sinc by Dave Martindale et alia are available here:
*
* http://www.binbooks.com/books/photo/i/l/57186AF8DE
* http://www.binbooks.com/books/photo/i/l/57186AE7E6&orig=1
*/
class lanczos : public filter {
private:
ale_pos half_width;
/*
* Lanczos function
*
* The lanczos function is the central lobe of the sinc function.
*/
ale_real _lanczos(ale_pos p) const {
if (fabs(p) >= half_width)
return 0;
return sinc::_sinc(p / half_width);
}
ale_real _lanczos(point p) const {
return _lanczos(p[0]) * _lanczos(p[1]);
}
public:
/*
* Size of filter support, in number of half-cycles to each side of the
* filter center.
*/
ale_pos support() const {
return half_width;
}
virtual int equals(const filter *f) const {
if (typeid(*f) == typeid(*this))
return ((lanczos *)f)->half_width == half_width;
return 0;
}
/*
* Response of filter at point p
*/
virtual ale_real response(point p) const {
return _lanczos(p);
}
lanczos(double half_width) {
this->half_width = half_width;
}
};
#endif

75
d2/filter/mult.h Normal file
View File

@@ -0,0 +1,75 @@
// 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
*/
#ifndef __mult_h__
#define __mult_h__
/*
* A class for pointwise multiplication of filters.
*/
class mult : public filter {
private:
ale_pos _support;
filter *f1, *f2;
public:
/*
* Size of filter support, in number of half-cycles to each side of the
* filter center.
*/
virtual ale_pos support() const {
return _support;
}
virtual int equals(const filter *f) const {
if (typeid(*f) != typeid(*this))
return 0;
const mult *m = (const mult *)f;
/*
* XXX: if we wished, we could recognize commutativity.
*/
return (m->f1->equals(f1)
&& m->f2->equals(f2));
}
/*
* Response of filter at point p
*/
virtual ale_real response(point p) const {
return f1->response(p) * f2->response(p);
}
mult(filter *f1, filter *f2) {
this->f1 = f1;
this->f2 = f2;
assert (f1 != NULL);
assert (f2 != NULL);
_support = f1->support();
if (f2->support() < _support)
_support = f2->support();
}
};
#endif

477
d2/filter/scaled_filter.h Normal file
View File

@@ -0,0 +1,477 @@
// 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
*/
#ifndef __scaled_filter_h__
#define __scaled_filter_h__
#include "filter.h"
#include "mult.h"
#include "sinc.h"
#include "triangle.h"
#include "box.h"
#include "gauss.h"
#include "lanczos.h"
/*
* Useful constants.
*/
// static const ale_pos sqrt2 = (ale_pos) 1.41421356237309504880;
// static const ale_pos one_over_sqrt2 = (ale_pos) 0.70710678118654752440;
static const ale_pos sqrt2 = sqrt((ale_pos) 2);
static const ale_pos one_over_sqrt2 = 1 / sqrt2;
/*
* Scaled filter class.
*/
class scaled_filter {
private:
/*
* Frequency limit:
*
* 0: indicates the higher limit (identical to output frequency)
*
* 1: indicates the safer limit (minimum of input and output
* frequencies)
*
* 2: indicates a limit dynamically derived from subsequent chain
* elements.
*/
int frequency_limit;
filter *f;
/*
* filter function parameters
*
* Parameters include data regarding the current image and
* transformation. All parameters are mutable.
*/
/*
* A pointer to the current image is stored, as well as the image
* offset and bayer type.
*/
mutable const image *im;
mutable unsigned int bayer;
mutable point offset;
/* We are either using one transformation or a combination of two
* transformations. T_TWO indicates whether two transformations are
* being used. T[] stores the transformations. When only one
* transformation is used, OFFSET stores the image offset.
*/
mutable unsigned int t_two;
mutable transformation t0, t1;
mutable int _is_projective;
/*
* Transform a point using the current transformation.
*/
point transform(point p, const trans_single &ts0, const trans_single &ts1) const {
if (t_two)
return ts1.unscaled_inverse_transform(ts0.transform_unscaled(p));
return ts0.transform_unscaled(p);
}
/*
* Inverse of the above.
*/
point transform_inverse(point p, const trans_single &ts0, const trans_single &ts1) const {
if (t_two)
return ts0.unscaled_inverse_transform(ts1.transform_unscaled(p));
return ts0.unscaled_inverse_transform(p);
}
/*
* Returns non-zero if the transformation might be non-Euclidean.
*/
int is_projective() const {
return _is_projective;
}
/*
* If we are limiting to source frequencies, then scale a filter to
* accept only frequencies we know to be expressible in the source.
* (Or do this approximately.)
*/
void freq_limit(point p, point mapped_p, ale_pos *hscale_g,
ale_pos *hscale_rb, ale_pos *wscale_g, ale_pos *wscale_rb,
const trans_single &ts0, const trans_single &ts1) const {
if (frequency_limit == 0)
return;
ale_pos hnorm, wnorm;
point dh = transform_inverse(p + point(1, 0), ts0, ts1);
point dw = transform_inverse(p + point(0, 1), ts0, ts1);
hnorm = (mapped_p - dh).norm();
wnorm = (mapped_p - dw).norm();
if (bayer == IMAGE_BAYER_NONE) {
if (hnorm < 1) {
*hscale_g = hnorm;
*hscale_rb = hnorm;
}
if (wnorm < 1) {
*wscale_g = wnorm;
*wscale_rb = wnorm;
}
} else {
if (hnorm < sqrt2) {
*hscale_g = hnorm / sqrt2;
*hscale_rb = hnorm / 2;
} else if (hnorm < 2) {
*hscale_rb = hnorm / 2;
}
if (wnorm < sqrt2) {
*wscale_g = wnorm / sqrt2;
*wscale_rb = wnorm / 2;
} else if (wnorm < 2) {
*wscale_rb = wnorm / 2;
}
}
}
void filter_channel(point p, point mapped_p, unsigned int k, ale_pos hscale,
ale_pos wscale, pixel *result, pixel *weight, int honor_exclusion,
int frame, ale_real prev_value, ale_real prev_weight,
const trans_single &ts0, const trans_single &ts1) const {
ale_real temp_result = (*result)[k], temp_weight = (*weight)[k];
ale_real certainty;
if (prev_weight > 0)
certainty = im->exp().confidence(k, prev_value);
else
certainty = 1; /* We calculate certainty later */
#if 1
/*
* This test matches the technical description.
*/
if (hscale < 1 && wscale < 1) {
#else
/*
* This approach is ~33% faster for Euclidean transformations,
* but is likely to produce different results in some cases.
*/
if (hscale <= 1 && wscale <= 1) {
#endif
/*
* Handle the especially coarse case.
*/
ale_pos fscale;
if (frequency_limit) {
fscale = (bayer == IMAGE_BAYER_NONE)
? (ale_pos) 1
: (k == 1) ? sqrt2 : (ale_pos) 2;
} else {
fscale = 1;
}
int min_i, max_i, min_j, max_j;
ale_pos support = f->support() * fscale;
if (1 / support == 0) {
min_i = 0;
max_i = im->height() - 1;
min_j = 0;
max_j = im->width() - 1;
} else {
point min = mapped_p - point(support, support);
point max = mapped_p + point(support, support);
/*
* lrintf() may be faster than ceil/floor() on some architectures.
* See render/psf/raster.h for more details.
*/
min_i = (int) lrintf(min[0]);
max_i = (int) lrintf(max[0]);
min_j = (int) lrintf(min[1]);
max_j = (int) lrintf(max[1]);
if (min_i < 0)
min_i = 0;
if (max_i > (int) im->height() - 1)
max_i = (int) im->height() - 1;
if (min_j < 0)
min_j = 0;
if (max_j > (int) im->width() - 1)
max_j = (int) im->width() - 1;
}
/*
* Iterate over the source pixels.
*/
for (int i = min_i; i <= max_i; i++)
for (int j = min_j; j <= max_j; j++) {
if (honor_exclusion && render::is_excluded_f(i, j, frame))
continue;
if (bayer != IMAGE_BAYER_NONE
&& (im->get_channels(i, j) & (1 << k)) == 0)
continue;
point a = point(i, j);
ale_real v = im->get_chan(i, j, k);
ale_real response = f->response((a - mapped_p) / fscale);
ale_real w = certainty * response;
temp_weight += w;
temp_result += w * v;
}
} else {
/*
* Establish the boundaries of filtering in the source.
*/
point min = mapped_p;
point max = mapped_p;
int imin[2];
int imax[2];
ale_pos sup = f->support();
if (1 / sup == 0) {
imin[0] = 0;
imax[0] = im->height() - 1;
imin[1] = 0;
imax[1] = im->width() - 1;
} else {
ale_pos hsup = sup / hscale;
ale_pos wsup = sup / wscale;
for (int ii = -1; ii <= +1; ii+=2)
for (int jj = -1; jj <= +1; jj+=2) {
point b = transform_inverse(point(hsup * ii, wsup * jj) + p, ts0, ts1);
for (int d = 0; d < 2; d++) {
if (b[d] < min[d])
min[d] = b[d];
if (b[d] > max[d])
max[d] = b[d];
}
}
/*
* lrintf() may be faster than ceil/floor() on some architectures.
* See render/psf/raster.h for more details.
*/
imin[0] = lrintf(min[0]);
imax[0] = lrintf(max[0]);
imin[1] = lrintf(min[1]);
imax[1] = lrintf(max[1]);
if (imin[0] < 0)
imin[0] = 0;
if (imax[0] > (int) im->height() - 1)
imax[0] = (int) im->height() - 1;
if (imin[1] < 0)
imin[1] = 0;
if (imax[1] > (int) im->width() - 1)
imax[1] = (int) im->width() - 1;
}
/*
* Iterate over the source pixels.
*/
for (int i = imin[0]; i <= imax[0]; i++)
for (int j = imin[1]; j <= imax[1]; j++) {
if (honor_exclusion && render::is_excluded_f(i, j, frame))
continue;
if (bayer != IMAGE_BAYER_NONE
&& (im->get_channels(i, j) & (1 << k)) == 0)
continue;
point a = transform(point(i, j), ts0, ts1);
ale_real v = im->get_chan(i, j, k);
ale_real response = f->response((a - p) * point(hscale, wscale));
ale_real w = certainty * response;
temp_weight += w;
temp_result += w * v;
}
}
if (!(prev_weight > 0)
&& d2::exposure::get_confidence() != 0
&& temp_weight > 0) {
/*
* Calculate certainty for the first pass.
*/
certainty = im->exp().confidence(k, temp_result / temp_weight);
temp_weight *= certainty;
temp_result *= certainty;
}
(*result)[k] = temp_result;
(*weight)[k] = temp_weight;
}
public:
scaled_filter(filter *f, int frequency_limit) :
t0(transformation::eu_identity()),
t1(transformation::eu_identity()) {
this->frequency_limit = frequency_limit;
this->f = f;
}
const filter *get_filter() const {
return f;
}
int equals(scaled_filter *s) {
return (frequency_limit == s->frequency_limit
&& f->equals(s->f));
}
int is_coarse() const {
return frequency_limit == 1;
}
int is_fine() const {
return frequency_limit == 0;
}
int is_dynamic() const {
return frequency_limit == 2;
}
/*
* Set the transformation T and image IM.
*/
void set_parameters(transformation _t, const image *_im, point _offset) const {
t_two = 0;
t0 = _t;
im = _im;
bayer = im->get_bayer();
offset = _offset;
_is_projective = _t.is_projective();
}
/*
* Set the transformations T and S, and image IM.
*/
void set_parameters(transformation _t, transformation _s, const image *_im) const {
t_two = 1;
t0 = _t;
t1 = _s;
im = _im;
bayer = im->get_bayer();
offset = point(0, 0);
_is_projective = t0.is_projective() || t1.is_projective();
}
/*
* Return filtered RESULT and WEIGHT at point P in a coordinate system
* specified by the inverse of transformation T based on data taken
* from image IM.
*/
void filtered(int i, int j, pixel *result, pixel *weight,
int honor_exclusion, int frame,
pixel prev_value = pixel(0, 0, 0),
pixel prev_weight = pixel(0, 0, 0)) const {
trans_single ts0, ts1;
pixel tm;
point p = point(i, j) + offset;
*result = pixel(0, 0, 0);
*weight = pixel(0, 0, 0);
ale_pos hscale_g = 1;
ale_pos hscale_rb = 1;
ale_pos wscale_g = 1;
ale_pos wscale_rb = 1;
if (t_two) {
ts1 = t1.t_at_point(p);
ts0 = t0.t_at_inv_point(ts1.transform_unscaled(p));
tm = ts0.get_tonal_multiplier(point(0, 0)) / ts1.get_tonal_multiplier(point(0, 0));
} else {
ts0 = t0.t_at_inv_point(p);
tm = ts0.get_tonal_multiplier(point(0, 0));
}
prev_value /= tm;
point mapped_p = transform_inverse(p, ts0, ts1);
/*
* Allowing points such as these results in problems that are
* annoying to resolve. Solving such problems is not obviously
* worth the effort, given that they can be avoided entirely by
* disallowing the points.
*/
if (mapped_p[0] < 0 || mapped_p[0] > im->height() - 1
|| mapped_p[1] < 0 || mapped_p[1] > im->width() - 1)
return;
freq_limit(p, mapped_p, &hscale_g, &hscale_rb, &wscale_g, &wscale_rb, ts0, ts1);
filter_channel(p, mapped_p, 0, hscale_rb, wscale_rb, result, weight, honor_exclusion, frame, prev_value[0], prev_weight[0], ts0, ts1);
filter_channel(p, mapped_p, 2, hscale_rb, hscale_rb, result, weight, honor_exclusion, frame, prev_value[2], prev_weight[2], ts0, ts1);
filter_channel(p, mapped_p, 1, hscale_g , hscale_g , result, weight, honor_exclusion, frame, prev_value[1], prev_weight[1], ts0, ts1);
(*result) *= tm;
}
};
#endif

76
d2/filter/sinc.h Normal file
View File

@@ -0,0 +1,76 @@
// 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
*/
#ifndef __sinc_h__
#define __sinc_h__
/*
* A filtering class for the sinc function.
*
* Notes on Lanczos and Sinc by Dave Martindale et alia are available here:
*
* http://www.binbooks.com/books/photo/i/l/57186AF8DE
* http://www.binbooks.com/books/photo/i/l/57186AE7E6&orig=1
*/
class sinc : public filter {
public:
/*
* Sinc filter.
*
* Width is infinite.
*/
static double _sinc(ale_pos p) {
if (fabs(p) < 0.001)
return 1;
return sin(M_PI * p) / (M_PI * p);
}
static double _sinc(point p) {
return _sinc(p[0]) * _sinc(p[1]);
}
virtual int equals(const filter *f) const {
if (typeid(*f) == typeid(*this))
return 1;
return 0;
}
/*
* Size of filter support, in number of half-cycles to each side of the
* filter center.
*/
virtual ale_pos support() const {
double zero = 0;
double infinity = 1 / zero;
assert (!isnan(infinity));
assert (1 < infinity);
return (ale_pos) infinity;
}
/*
* Response of filter at point p
*/
virtual ale_real response(point p) const {
return _sinc(p);
}
};
#endif

98
d2/filter/ssfe.h Normal file
View File

@@ -0,0 +1,98 @@
// 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
*/
#ifndef __ssfe_h__
#define __ssfe_h__
#include "scaled_filter.h"
#include "filter.h"
/*
* Scaled filter class with exclusion.
*/
class ssfe {
private:
/*
* Honor exclusion?
*/
int honor_exclusion;
scaled_filter *f;
mutable point _offset;
mutable int have_offset;
public:
ssfe(scaled_filter *f, int honor_exclusion) {
this->honor_exclusion = honor_exclusion;
this->f = f;
}
const scaled_filter *get_scaled_filter() const {
return f;
}
int equals(const ssfe *s) {
return (honor_exclusion == s->honor_exclusion
&& f->equals(s->f));
}
int ex_is_honored() const {
return honor_exclusion;
}
/*
* Set the parameters for filtering.
*/
void set_parameters(transformation t, const image *im, point offset) const {
have_offset = 1;
_offset = offset;
f->set_parameters(t, im, offset);
}
void set_parameters(transformation t, transformation s, const image *im) const {
have_offset = 0;
f->set_parameters(t, s, im);
}
/*
* Return filtered RESULT and WEIGHT at point P in a coordinate system
* specified by the inverse of transformation T based on data taken
* from image IM.
*/
void filtered(int i, int j, int frame, pixel *result,
pixel *weight, pixel prev_value = pixel(0, 0, 0), pixel prev_weight = pixel(0, 0, 0)) const {
/*
* We need a valid offset in order to determine exclusion
* regions.
*/
assert (have_offset || !honor_exclusion);
*result = pixel(0, 0, 0);
*weight = pixel(0, 0, 0);
if (honor_exclusion && render::is_excluded_r(_offset, i, j, frame))
return;
f->filtered(i, j, result, weight, honor_exclusion, frame, prev_value, prev_weight);
}
};
#endif

76
d2/filter/triangle.h Normal file
View File

@@ -0,0 +1,76 @@
// 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
*/
#ifndef __triangle_h__
#define __triangle_h__
/*
* A triangle filter class.
*/
class triangle : public filter {
private:
ale_pos half_width;
/*
* Triangle filter.
*/
ale_real _triangle(ale_pos p) const {
ale_pos fabs_p = fabs(p);
if (fabs_p >= half_width)
return 0;
return (1 - fabs_p / half_width);
}
ale_real _triangle(point p) const {
return _triangle(p[0]) * _triangle(p[1]);
}
public:
/*
* Size of filter support, in number of half-cycles to each side of the
* filter center.
*/
virtual ale_pos support() const {
return half_width;
}
virtual int equals(const filter *f) const {
if (typeid(*f) == typeid(*this))
return ((triangle *)f)->half_width == half_width;
return 0;
}
/*
* Response of filter at point p
*/
virtual ale_real response(point p) const {
return _triangle(p);
}
triangle(ale_pos half_width) {
this->half_width = half_width;
}
};
#endif

52
d2/filter/zero.h Normal file
View File

@@ -0,0 +1,52 @@
// 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
*/
#ifndef __zero_h__
#define __zero_h__
/*
* A zero filter.
*/
class zero : public filter {
public:
/*
* Size of filter support, in number of half-cycles to each side of the
* filter center.
*/
ale_pos support() const {
return 0;
}
virtual int equals(const filter *f) const {
if (typeid(*f) == typeid(*this))
return 1;
return 0;
}
/*
* Response of filter at point p
*/
virtual ale_real response(point p) const {
return 0;
}
};
#endif

27
d2/image.cc Normal file
View File

@@ -0,0 +1,27 @@
// Copyright 2007 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 "image.h"
/*
* See image.h for details on these variables.
*/
double image::resident = 0;

1120
d2/image.h Normal file

File diff suppressed because it is too large Load Diff

420
d2/image_ale_real.h Normal file
View File

@@ -0,0 +1,420 @@
// Copyright 2002, 2003, 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
*/
/*
* image_ale_real.h: Image represented by an array of ale_reals
*/
#ifndef __image_ale_real_h__
#define __image_ale_real_h__
#include "exposure/exposure.h"
#include "point.h"
#include "image.h"
#define RESIDENT_DIVISIONS 200
template <int disk_support>
class image_ale_real : public image {
private:
/*
* Data structures without file support.
*/
spixel *_p;
/*
* Data structures for file support.
*/
FILE *support;
mutable spixel *_p_segments[RESIDENT_DIVISIONS];
mutable int dirty_segments[RESIDENT_DIVISIONS];
mutable int resident_list[RESIDENT_DIVISIONS];
mutable int resident_next;
int resident_max;
int rows_per_segment;
mutable thread::rwlock_t rwlock;
public:
/*
* Wrapper encapsulating details of the separation between the
* resident-checking implementation and non-checking.
*/
static image *new_image_ale_real(unsigned int dimy,
unsigned int dimx,
unsigned int depth,
const char *name = "anonymous",
exposure *exp = NULL) {
double resident = image::get_resident();
if (resident == 0 || resident * 1000000 >= dimy * dimx)
return new image_ale_real<0>(dimy, dimx, depth, name, exp);
return new image_ale_real<1>(dimy, dimx, depth, name, exp);
}
image_ale_real (unsigned int dimy, unsigned int dimx, unsigned int
depth, const char *name = "anonymous", exposure *exp = NULL)
: image(dimy, dimx, depth, name, exp) {
if (disk_support == 0) {
_p = new spixel[dimx * dimy];
assert (_p);
if (!_p) {
fprintf(stderr, "Could not allocate memory for image data.\n");
exit(1);
}
} else {
rows_per_segment = (int) ceil((double) dimy / (double) RESIDENT_DIVISIONS);
assert (rows_per_segment > 0);
for (int i = 0; i < RESIDENT_DIVISIONS; i++) {
_p_segments[i] = NULL;
dirty_segments[i] = 0;
resident_list[i] = -1;
}
resident_max = (unsigned int) floor((image::get_resident() * 1000000)
/ (rows_per_segment * dimx));
assert (resident_max <= RESIDENT_DIVISIONS);
if (resident_max == 0) {
ui::get()->error_hint(
"No segments resident in image array.",
"Try recompiling with more RESIDENT_DIVISIONS");
}
resident_next = 0;
support = tmpfile();
if (!support) {
ui::get()->error_hint(
"Unable to create temporary file to support image array.",
"Set --resident 0, or Win32/64 users might run as admin.");
}
spixel *zero = new spixel[dimx];
assert(zero);
for (unsigned int i = 0; i < dimy; i++) {
unsigned int c = fwrite(zero, sizeof(spixel), dimx, support);
if (c < dimx)
ui::get()->error_hint("Image array support file error.",
"Submit a bug report.");
}
delete[] zero;
}
}
virtual ~image_ale_real() {
if (disk_support == 0) {
delete[] _p;
} else {
for (int i = 0; i < RESIDENT_DIVISIONS; i++) {
if (_p_segments[i])
delete[] _p_segments[i];
}
fclose(support);
}
}
void resident_begin(unsigned int segment) const {
rwlock.rdlock();
if (_p_segments[segment])
return;
rwlock.unlock();
rwlock.wrlock();
if (_p_segments[segment])
return;
if (resident_list[resident_next] >= 0) {
/*
* Eject a segment
*/
if (dirty_segments[resident_list[resident_next]]) {
fseek(support, rows_per_segment * _dimx * sizeof(spixel)
* resident_list[resident_next],
SEEK_SET);
assert(_p_segments[resident_list[resident_next]]);
fwrite(_p_segments[resident_list[resident_next]],
sizeof(spixel), rows_per_segment * _dimx, support);
dirty_segments[resident_list[resident_next]] = 0;
}
delete[] _p_segments[resident_list[resident_next]];
_p_segments[resident_list[resident_next]] = NULL;
}
resident_list[resident_next] = segment;
_p_segments[segment] = new spixel[_dimx * rows_per_segment];
assert (_p_segments[segment]);
fseek(support, rows_per_segment * _dimx * sizeof(spixel)
* segment,
SEEK_SET);
fread(_p_segments[segment], sizeof(spixel), rows_per_segment * _dimx, support);
/*
* Update the next ejection candidate.
*/
resident_next++;
if (resident_next >= resident_max)
resident_next = 0;
}
void resident_end(unsigned int segment) const {
rwlock.unlock();
}
spixel get_pixel(unsigned int y, unsigned int x) const {
assert (x < _dimx);
assert (y < _dimy);
if (disk_support == 0) {
return _p[y * _dimx + x];
} else {
int segment = y / rows_per_segment;
assert (segment < RESIDENT_DIVISIONS);
resident_begin(segment);
spixel result = _p_segments[segment][(y % rows_per_segment) * _dimx + x];
resident_end(segment);
return result;
}
}
void set_pixel(unsigned int y, unsigned int x, spixel p) {
assert (x < _dimx);
assert (y < _dimy);
image_updated();
if (disk_support == 0) {
_p[y * _dimx + x] = p;
} else {
int segment = y / rows_per_segment;
assert (segment < RESIDENT_DIVISIONS);
resident_begin(segment);
_p_segments[segment][(y % rows_per_segment) * _dimx + x] = p;
dirty_segments[segment] = 1;
resident_end(segment);
}
}
void mul_pixel(unsigned int y, unsigned int x, spixel p) {
assert (x < _dimx);
assert (y < _dimy);
image_updated();
if (disk_support == 0) {
_p[y * _dimx + x] *= p;
} else {
int segment = y / rows_per_segment;
assert (segment < RESIDENT_DIVISIONS);
resident_begin(segment);
_p_segments[segment][(y % rows_per_segment) * _dimx + x] *= p;
dirty_segments[segment] = 1;
resident_end(segment);
}
}
void add_pixel(unsigned int y, unsigned int x, pixel p) {
assert (x < _dimx);
assert (y < _dimy);
image_updated();
if (disk_support == 0) {
_p[y * _dimx + x] += p;
} else {
int segment = y / rows_per_segment;
assert (segment < RESIDENT_DIVISIONS);
resident_begin(segment);
_p_segments[segment][(y % rows_per_segment) * _dimx + x] += p;
dirty_segments[segment] = 1;
resident_end(segment);
}
}
ale_sreal get_chan(unsigned int y, unsigned int x, unsigned int k) const {
assert (x < _dimx);
assert (y < _dimy);
if (disk_support == 0) {
return _p[y * _dimx + x][k];
} else {
int segment = y / rows_per_segment;
assert (segment < RESIDENT_DIVISIONS);
resident_begin(segment);
ale_sreal result = _p_segments[segment]
[(y % rows_per_segment) * _dimx + x][k];
resident_end(segment);
return result;
}
}
void set_chan(unsigned int y, unsigned int x, unsigned int k, ale_sreal c) {
assert (x < _dimx);
assert (y < _dimy);
image_updated();
if (disk_support == 0) {
_p[y * _dimx + x][k] = c;
} else {
int segment = y / rows_per_segment;
assert (segment < RESIDENT_DIVISIONS);
resident_begin(segment);
_p_segments[segment][(y % rows_per_segment) * _dimx + x][k] = c;
dirty_segments[segment] = 1;
resident_end(segment);
}
}
void div_chan(unsigned int y, unsigned int x, unsigned int k, ale_sreal c) {
assert (x < _dimx);
assert (y < _dimy);
image_updated();
if (disk_support == 0) {
_p[y * _dimx + x][k] /= c;
} else {
int segment = y / rows_per_segment;
assert (segment < RESIDENT_DIVISIONS);
resident_begin(segment);
_p_segments[segment][(y % rows_per_segment) * _dimx + x][k] /= c;
dirty_segments[segment] = 1;
resident_end(segment);
}
}
/*
* Make a new image suitable for receiving scaled values.
*/
virtual image *scale_generator(int height, int width, int depth, const char *name) const {
return new_image_ale_real(height, width, depth, name, _exp);
}
/*
* Extend the image area to the top, bottom, left, and right,
* initializing the new image areas with black pixels. Negative values
* shrink the image.
*/
image *_extend(int top, int bottom, int left, int right) {
image *is = new_image_ale_real (
height() + top + bottom,
width() + left + right , depth(), name, _exp);
assert(is);
unsigned int min_i = (-top > 0)
? -top
: 0;
unsigned int min_j = (-left > 0)
? -left
: 0;
unsigned int max_i = (height() < is->height() - top)
? height()
: is->height() - top;
unsigned int max_j = (width() < is->width() - left)
? width()
: is->width() - left;
for (unsigned int i = min_i; i < max_i; i++)
for (unsigned int j = min_j; j < max_j; j++)
is->set_pixel(i + top, j + left, get_pixel(i, j));
is->set_offset(_offset[0] - top, _offset[1] - left);
return is;
}
private:
void trigger(pixel multiplier) {
for (unsigned int i = 0; i < height(); i++)
for (unsigned int j = 0; j < width(); j++) {
mul_pixel(i, j, multiplier);
}
}
};
/*
* Wrapper encapsulating details of the separation between the
* resident-checking implementation and non-checking.
*/
static inline image *new_image_ale_real(unsigned int dimy,
unsigned int dimx,
unsigned int depth,
const char *name = "anonymous",
exposure *exp = NULL) {
return image_ale_real<0>::new_image_ale_real(dimy, dimx, depth, name, exp);
}
#endif

465
d2/image_bayer_ale_real.h Normal file
View File

@@ -0,0 +1,465 @@
// Copyright 2002, 2003, 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
*/
/*
* image_bayer_ale_real.h: Bayer-patterned image represented by an array of ale_reals
*/
#ifndef __image_bayer_ale_real_h__
#define __image_bayer_ale_real_h__
#include "exposure/exposure.h"
#include "point.h"
#include "image.h"
#include "image_ale_real.h"
template <int disk_support>
class image_bayer_ale_real : public image {
private:
/*
* Data structures without file support.
*/
ale_sreal *_p;
/*
* Data structures for file support.
*/
FILE *support;
mutable ale_sreal *_p_segments[RESIDENT_DIVISIONS];
mutable int dirty_segments[RESIDENT_DIVISIONS];
mutable int resident_list[RESIDENT_DIVISIONS];
mutable int resident_next;
int resident_max;
int rows_per_segment;
mutable thread::rwlock_t rwlock;
private:
/*
* X offset of 'R' element
*/
unsigned int r_x_offset() const {
return bayer & 0x1;
}
/*
* Y offset of 'R' element
*/
unsigned int r_y_offset() const {
return (bayer & 0x2) >> 1;
}
public:
/*
* Return the color of a given pixel.
*/
unsigned int bayer_color(unsigned int i, unsigned int j) const {
return (i + r_y_offset()) % 2 + (j + r_x_offset()) % 2;
}
char get_channels(int i, int j) const {
return (1 << bayer_color(i, j));
}
public:
/*
* Wrapper encapsulating details of the separation between the
* resident-checking implementation and non-checking.
*/
static inline image *new_image_bayer_ale_real(unsigned int dimy,
unsigned int dimx,
unsigned int depth,
unsigned int bayer,
const char *name = "anonymous",
exposure *exp = NULL) {
unsigned int resident = image::get_resident();
if (resident == 0 || resident * 1000000 > dimy * dimx)
return new image_bayer_ale_real<0>(dimy, dimx, depth, bayer, name, exp);
return new image_bayer_ale_real<1>(dimy, dimx, depth, bayer, name, exp);
}
image_bayer_ale_real (unsigned int dimy, unsigned int dimx, unsigned int depth,
unsigned int bayer, const char *name = "anonymous", exposure *exp = NULL)
: image(dimy, dimx, depth, name, exp, bayer) {
assert (bayer == IMAGE_BAYER_BGRG
|| bayer == IMAGE_BAYER_GBGR
|| bayer == IMAGE_BAYER_GRGB
|| bayer == IMAGE_BAYER_RGBG);
if (disk_support == 0) {
_p = new ale_sreal[dimx * dimy];
assert (_p);
if (!_p) {
fprintf(stderr, "Could not allocate memory for image data.\n");
exit(1);
}
for (unsigned int i = 0; i < dimx * dimy; i++) {
_p[i] = 0;
}
} else {
rows_per_segment = (int) ceil((double) dimy / (double) RESIDENT_DIVISIONS);
assert (rows_per_segment > 0);
for (int i = 0; i < RESIDENT_DIVISIONS; i++) {
_p_segments[i] = NULL;
dirty_segments[i] = 0;
resident_list[i] = -1;
}
resident_max = (unsigned int) floor((image::get_resident() * 1000000)
/ (rows_per_segment * dimx));
assert (resident_max <= RESIDENT_DIVISIONS);
if (resident_max == 0) {
ui::get()->error_hint(
"No segments resident in image array.",
"Try recompiling with more RESIDENT_DIVISIONS");
}
resident_next = 0;
support = tmpfile();
if (!support) {
ui::get()->error_hint(
"Unable to create temporary file to support image array.",
"Set --resident 0, or Win32/64 users might run as admin.");
}
ale_sreal *zero = new ale_sreal[dimx];
assert(zero);
for (unsigned int i = 0; i < dimx; i++)
zero[i] = 0;
for (unsigned int i = 0; i < dimy; i++) {
unsigned int c = fwrite(zero, sizeof(ale_sreal), dimx, support);
if (c < dimx)
ui::get()->error_hint("Image array support file error.",
"Submit a bug report.");
}
delete[] zero;
}
}
virtual ~image_bayer_ale_real() {
if (disk_support == 0) {
delete[] _p;
} else {
for (int i = 0; i < RESIDENT_DIVISIONS; i++) {
if (_p_segments[i])
delete[] _p_segments[i];
}
fclose(support);
}
}
void resident_begin(unsigned int segment) const {
rwlock.rdlock();
if (_p_segments[segment])
return;
rwlock.unlock();
rwlock.wrlock();
if (_p_segments[segment])
return;
if (resident_list[resident_next] >= 0) {
/*
* Eject a segment
*/
if (dirty_segments[resident_list[resident_next]]) {
fseek(support, rows_per_segment * _dimx * sizeof(ale_sreal)
* resident_list[resident_next],
SEEK_SET);
assert(_p_segments[resident_list[resident_next]]);
fwrite(_p_segments[resident_list[resident_next]],
sizeof(ale_sreal), rows_per_segment * _dimx, support);
dirty_segments[resident_list[resident_next]] = 0;
}
delete[] _p_segments[resident_list[resident_next]];
_p_segments[resident_list[resident_next]] = NULL;
}
resident_list[resident_next] = segment;
_p_segments[segment] = new ale_sreal[_dimx * rows_per_segment];
assert (_p_segments[segment]);
fseek(support, rows_per_segment * _dimx * sizeof(ale_sreal)
* segment,
SEEK_SET);
fread(_p_segments[segment], sizeof(ale_sreal), rows_per_segment * _dimx, support);
/*
* Update the next ejection candidate.
*/
resident_next++;
if (resident_next >= resident_max)
resident_next = 0;
}
void resident_end(unsigned int segment) const {
rwlock.unlock();
}
void set_chan(unsigned int y, unsigned int x, unsigned int k, ale_sreal c) {
assert (k == bayer_color(y, x));
if (disk_support == 0) {
_p[y * _dimx + x] = c;
} else {
int segment = y / rows_per_segment;
assert (segment < RESIDENT_DIVISIONS);
resident_begin(segment);
_p_segments[segment][(y % rows_per_segment) * _dimx + x] = c;
dirty_segments[segment] = 1;
resident_end(segment);
}
}
void add_chan(unsigned int y, unsigned int x, unsigned int k, ale_sreal c) {
assert (k == bayer_color(y, x));
if (disk_support == 0) {
_p[y * _dimx + x] += c;
} else {
int segment = y / rows_per_segment;
assert (segment < RESIDENT_DIVISIONS);
resident_begin(segment);
_p_segments[segment][(y % rows_per_segment) * _dimx + x] += c;
dirty_segments[segment] = 1;
resident_end(segment);
}
}
void div_chan(unsigned int y, unsigned int x, unsigned int k, ale_sreal c) {
assert (k == bayer_color(y, x));
if (disk_support == 0) {
_p[y * _dimx + x] /= c;
} else {
int segment = y / rows_per_segment;
assert (segment < RESIDENT_DIVISIONS);
resident_begin(segment);
_p_segments[segment][(y % rows_per_segment) * _dimx + x] /= c;
dirty_segments[segment] = 1;
resident_end(segment);
}
}
ale_sreal get_chan(unsigned int y, unsigned int x, unsigned int k) const {
#if 0
/*
* This may be expensive.
*/
assert (k == bayer_color(y, x));
#endif
if (disk_support == 0) {
return _p[y * _dimx + x];
} else {
int segment = y / rows_per_segment;
assert (segment < RESIDENT_DIVISIONS);
resident_begin(segment);
ale_sreal result = _p_segments[segment]
[(y % rows_per_segment) * _dimx + x];
resident_end(segment);
return result;
}
}
/*
* This method throws away data not stored at this pixel
* position.
*/
void set_pixel(unsigned int y, unsigned int x, spixel p) {
set_chan(y, x, bayer_color(y, x), p[bayer_color(y, x)]);
}
/*
* This method uses bilinear interpolation.
*/
spixel get_pixel(unsigned int y, unsigned int x) const {
pixel result;
unsigned int k = bayer_color(y, x);
ale_real sum;
unsigned int num;
result[k] = get_chan(y, x, k);
if (k == 1) {
unsigned int k1 = bayer_color(y + 1, x);
unsigned int k2 = 2 - k1;
sum = 0; num = 0;
if (y > 0) {
sum += get_chan(y - 1, x, k1);
num++;
}
if (y < _dimy - 1) {
sum += get_chan(y + 1, x, k1);
num++;
}
assert (num > 0);
result[k1] = sum / num;
sum = 0; num = 0;
if (x > 0) {
sum += get_chan(y, x - 1, k2);
num++;
}
if (x < _dimx - 1) {
sum += get_chan(y, x + 1, k2);
num++;
}
assert (num > 0);
result[k2] = sum / num;
return result;
}
sum = 0; num = 0;
if (y > 0) {
sum += get_chan(y - 1, x, 1);
num++;
}
if (x > 0) {
sum += get_chan(y, x - 1, 1);
num++;
}
if (y < _dimy - 1) {
sum += get_chan(y + 1, x, 1);
num++;
}
if (x < _dimx - 1) {
sum += get_chan(y, x + 1, 1);
num++;
}
assert (num > 0);
result[1] = sum / num;
sum = 0; num = 0;
if (y > 0 && x > 0) {
sum += get_chan(y - 1, x - 1, 2 - k);
num++;
}
if (y > 0 && x < _dimx - 1) {
sum += get_chan(y - 1, x + 1, 2 - k);
num++;
}
if (y < _dimy - 1 && x > 0) {
sum += get_chan(y + 1, x - 1, 2 - k);
num++;
}
if (y < _dimy - 1 && x < _dimx - 1) {
sum += get_chan(y + 1, x + 1, 2 - k);
num++;
}
result[2 - k] = sum/num;
return result;
}
spixel get_raw_pixel(unsigned int y, unsigned int x) const {
pixel result;
int k = bayer_color(y, x);
result[k] = get_chan(y, x, k);
return result;
}
/*
* Make a new image suitable for receiving scaled values.
*/
virtual image *scale_generator(int height, int width, int depth, const char *name) const {
return new_image_ale_real(height, width, depth, name, _exp);
}
/*
* Extend the image area to the top, bottom, left, and right,
* initializing the new image areas with black pixels.
*/
image *_extend(int top, int bottom, int left, int right) {
/*
* Bayer-patterned images should always represent inputs,
* which should not ever be extended.
*/
assert(0);
return NULL;
}
private:
void trigger(pixel multiplier) {
for (unsigned int i = 0; i < _dimy; i++)
for (unsigned int j = 0; j < _dimx; j++) {
unsigned int k = bayer_color(i, j);
set_chan(i, j, k, get_chan(i, j, k) * multiplier[k]);
}
}
};
/*
* Wrapper encapsulating details of the separation between the
* resident-checking implementation and non-checking.
*/
static inline image *new_image_bayer_ale_real(unsigned int dimy,
unsigned int dimx,
unsigned int depth,
unsigned int bayer,
const char *name = "anonymous",
exposure *exp = NULL) {
return image_bayer_ale_real<0>::new_image_bayer_ale_real(dimy, dimx, depth, bayer, name, exp);
}
#endif

49
d2/image_rw.cc Normal file
View File

@@ -0,0 +1,49 @@
// 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 "image_rw.h"
/*
* See image_rw.h for details on these variables.
*/
int image_rw::ppm_type = 0;
unsigned int image_rw::mcv = 65535;
unsigned int image_rw::file_count = 0;
const char *image_rw::output_filename = NULL;
const char **image_rw::filenames = NULL;
const image **image_rw::images = NULL;
int *image_rw::files_open;
int image_rw::latest_close_num = -1;
double image_rw::cache_size = 0;
double image_rw::cache_size_max = 256;
unsigned int image_rw::cache_count = 0;
double image_rw::nn_defined_radius = 0;
exposure **image_rw::input_exposure = NULL;
exposure *image_rw::output_exposure = NULL;
unsigned int image_rw::bayer_default = 0;
unsigned int *image_rw::bayer_specific = NULL;
int image_rw::exposure_scale = 0;

647
d2/image_rw.h Normal file
View File

@@ -0,0 +1,647 @@
// 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
*/
/*
* image_rw.h: Read and write images.
*/
#ifndef __image_rw_h__
#define __image_rw_h__
#include "image.h"
#include "image_ale_real.h"
#include "image_bayer_ale_real.h"
#include "ppm.h"
#include "exposure/exposure.h"
#include "exposure/exposure_default.h"
class image_rw {
/*
* Private data members
*/
/*
* PPM type
*
* 0 = No type selected
* 1 = PPM Raw
* 2 = PPM Plain
*/
static int ppm_type;
/*
* Bit depth
*/
static unsigned int mcv;
/*
* Nearest-neighbor defined value radius.
*/
static double nn_defined_radius;
/*
* Input and output exposure models
*/
static exposure **input_exposure;
static exposure *output_exposure;
static int exposure_scale;
/*
* Default bayer pattern
*/
static unsigned int bayer_default;
/*
* Image-specific bayer patterns.
*/
static unsigned int *bayer_specific;
/*
* Pointer to the output filename
*/
static const char *output_filename;
/*
* Variables relating to input image files and image data structures.
*/
static const char **filenames;
static unsigned int file_count;
static const image **images;
static int *files_open;
/*
* The most recently closed image number.
*/
static int latest_close_num;
/*
* Maximum cache size, in megabytes (2^20 * bytes), for images not most
* recently closed.
*/
static double cache_size_max;
/*
* Actual cache size.
*/
static double cache_size;
/*
* Number of cached files.
*/
static unsigned int cache_count;
/*
* Private methods to init and shut down the file reader.
*/
/*
* Initialize the image file handler
*/
static void init_image() {
#ifdef USE_MAGICK
InitializeMagick("ale");
#endif
}
/*
* Destroy the image file handler
*/
static void destroy_image() {
#ifdef USE_MAGICK
DestroyMagick();
#endif
}
public:
/*
* Methods to read and write image files
*/
/*
* Read an image from a file
*/
static image *read_image(const char *filename, exposure *exp, const char *name = "file",
unsigned int bayer = IMAGE_BAYER_DEFAULT, int init_reference_gain = 0) {
static int warned = 0;
if (bayer == IMAGE_BAYER_DEFAULT)
bayer = bayer_default;
if (is_eppm(filename)) {
return read_ppm(filename, exp, bayer, init_reference_gain);
}
#ifdef USE_MAGICK
if (MaxRGB < 65535 && mcv == 65535 && !warned) {
fprintf(stderr, "\n\n*** Warning: " MagickPackageName " has not been compiled with 16 bit support.\n");
fprintf(stderr, "*** Reading input using 8 bits per channel.\n");
fprintf(stderr, "*** \n");
fprintf(stderr, "*** (To silence this warning, specify option --8bpc)\n\n\n");
warned = 1;
}
/*
* Patterned after http://www.imagemagick.org/www/api.html
* and http://www.imagemagick.org/www/smile.c
*/
ExceptionInfo exception;
Image *mi;
ImageInfo *image_info;
image *im;
const PixelPacket *p;
unsigned int i, j;
ale_real black_level = exp->get_black_level();
GetExceptionInfo(&exception);
image_info = CloneImageInfo((ImageInfo *) NULL);
strncpy(image_info->filename, filename, MaxTextExtent);
mi = ReadImage(image_info, &exception);
if (exception.severity != UndefinedException) {
fprintf(stderr, "\n\n");
CatchException(&exception);
fprintf(stderr, "\n");
}
if (mi == (Image *) NULL)
exit(1);
if (bayer == IMAGE_BAYER_NONE)
im = new_image_ale_real(mi->rows, mi->columns, 3, name, exp);
else
im = new_image_bayer_ale_real(mi->rows, mi->columns, 3, bayer, name, exp);
for (i = 0; i < mi->rows; i++) {
p = AcquireImagePixels(mi, 0, i, mi->columns, 1, &exception);
if (exception.severity != UndefinedException)
CatchException(&exception);
if (p == NULL)
exit(1);
for (j = 0; j < mi->columns; j++) {
pixel input ( ale_real_from_int(p->red, MaxRGB),
ale_real_from_int(p->green, MaxRGB),
ale_real_from_int(p->blue, MaxRGB) );
pixel linear_input = (exp->linearize(input) - exp->get_multiplier() * black_level)
/ (1 - black_level);
im->set_pixel(i, j, linear_input);
p++;
}
}
DestroyImage(mi);
DestroyImageInfo(image_info);
return im;
#else
return read_ppm(filename, exp, bayer);
#endif
}
/*
* Initializer.
*
* Handle FILE_COUNT input files with names in array FILENAMES and
* output file OUTPUT_FILENAME. FILENAMES should be an array of char *
* that is never freed. OUTPUT_FILENAME should be a char * that is
* never freed.
*
* INPUT_EXPOSURE should be an array of FILE_COUNT exposure objects
* that is never freed. OUTPUT_EXPOSURE should be an exposure * that
* is never freed.
*/
static void init(unsigned int _file_count, const char **_filenames,
const char *_output_filename, exposure **_input_exposure,
exposure *_output_exposure){
assert (_file_count > 0);
init_image();
filenames = _filenames;
file_count = _file_count;
output_filename = _output_filename;
input_exposure = _input_exposure;
output_exposure = _output_exposure;
images = (const image **)malloc(file_count * sizeof(image *));
bayer_specific = (unsigned int *)malloc(file_count * sizeof(unsigned int));
files_open = (int *)calloc(file_count, sizeof(int));
assert (images);
assert (bayer_specific);
assert (files_open);
if (!images || !files_open || !bayer_specific) {
fprintf(stderr, "Unable to allocate memory for images.\n");
exit(1);
}
for (unsigned int i = 0; i < file_count; i++)
bayer_specific[i] = IMAGE_BAYER_DEFAULT;
ui::get()->identify_output(output_filename);
}
static void ppm_plain() {
ppm_type = 2;
}
static void ppm_raw() {
ppm_type = 1;
}
static void ppm_auto() {
#ifdef USE_MAGICK
ppm_type = 0;
#else
fprintf(stderr, "\n\n*** Error: --auto flag not supported on this build. ***\n"
"*** (Hint: Rebuild with IMAGEMAGICK=1) ***\n\n");
exit(1);
#endif
}
static void set_default_bayer(unsigned int b) {
bayer_default = b;
}
static void set_specific_bayer(unsigned int index, unsigned int b) {
assert (bayer_specific);
bayer_specific[index] = b;
}
static void depth16() {
mcv = 65535;
}
static void depth8() {
mcv = 255;
}
static void set_cache(double size) {
cache_size_max = size;
}
static void destroy() {
assert (file_count > 0);
destroy_image();
}
static unsigned int count() {
assert (file_count > 0);
return file_count;
}
static const char *name(unsigned int image) {
assert (image < file_count);
return filenames[image];
}
static void def_nn(double _nn) {
nn_defined_radius = _nn;
}
static const char *output_name() {
assert (file_count > 0);
return output_filename;
}
/*
* Write an image to a file
*/
static void write_image(const char *filename, const image *im, exposure *exp = output_exposure, int rezero = 0, int exp_scale_override = 0) {
static int warned = 0;
/*
* Handle ALE-specific magical filenames.
*/
if (!strcmp(filename, "dump:")) {
fprintf(stderr, "Image dump: ");
for (unsigned int i = 0; i < im->height(); i++)
for (unsigned int j = 0; j < im->width(); j++) {
pixel p = im->get_pixel(i, j);
fprintf(stderr, "(%d, %d): [%f %f %f] ", i, j, (double) p[0], (double) p[1], (double) p[2]);
}
fprintf(stderr, "\n");
return;
}
#ifdef USE_MAGICK
/*
* Patterned after http://www.imagemagick.org/www/api.html
* and http://www.imagemagick.org/www/smile.c
*/
ExceptionInfo exception;
Image *mi;
ImageInfo *image_info;
PixelPacket *p;
unsigned int i, j;
GetExceptionInfo(&exception);
image_info = CloneImageInfo((ImageInfo *) NULL);
strncpy(image_info->filename, filename, MaxTextExtent);
mi = AllocateImage(image_info);
if (mi == (Image *) NULL)
MagickError(ResourceLimitError,
"Unable to display image", "MemoryAllocationFailed");
mi->columns = im->width();
mi->rows = im->height();
/*
* Set the output image depth
*/
if (MaxRGB < 65535 || mcv < 65535)
mi->depth = 8;
else
mi->depth = 16;
if (MaxRGB < 65535 && mcv == 65535 && !warned) {
fprintf(stderr, "\n\n*** Warning: " MagickPackageName " has not been compiled with 16 bit support.\n");
fprintf(stderr, "*** Writing output using 8 bits per channel.\n");
fprintf(stderr, "*** \n");
fprintf(stderr, "*** (To silence this warning, specify option --8bpc)\n\n\n");
warned = 1;
}
/*
* Set compression type
*/
if (ppm_type == 2) {
mi->compression = NoCompression;
image_info->compression = NoCompression;
strncpy(mi->magick, "PNM", MaxTextExtent);
strncpy(image_info->magick, "PNM", MaxTextExtent);
} else if (ppm_type == 1) {
strncpy(mi->magick, "PNM", MaxTextExtent);
strncpy(image_info->magick, "PNM", MaxTextExtent);
}
/*
* Automatic exposure adjustment (don't blow out highlights)
*/
ale_real maxval = 1;
ale_real minval = (rezero ? im->minval() : (ale_real) 0);
if (minval > 0)
minval = 0;
pixel minval_pixel(minval, minval, minval);
if (exposure_scale || exp_scale_override) {
ale_real new_maxval = im->maxval();
if (new_maxval > maxval)
maxval = new_maxval;
}
/*
* Write the image
*/
for (i = 0; i < mi->rows; i++) {
p = SetImagePixels(mi, 0, i, mi->columns, 1);
if (p == NULL)
break;
for (j = 0; j < mi->columns; j++) {
pixel value = im->get_pixel(i, j);
/*
* Get nearest-neighbor defined values.
*
* XXX: While this implementation is correct, it is inefficient
* for large radii. A better implementation would search
* perimeters of squares of ever-increasing radius, tracking
* the best-so-far data until the square perimeter exceeded the
* best-so-far radius.
*/
for (int k = 0; k < 3; k++)
if (isnan(value[k]))
for (int radius = 1; radius <= nn_defined_radius; radius++) {
double nearest_radius_squared = (radius + 1) * (radius + 1);
for (int ii = -radius; ii <= radius; ii++)
for (int jj = -radius; jj <= radius; jj++) {
if (!im->in_bounds(point(i + ii, j + jj)))
continue;
if (ii * ii + jj * jj < nearest_radius_squared
&& finite(im->get_pixel(i + ii, j + jj)[k])) {
value[k] = im->get_pixel(i + ii, j + jj)[k];
nearest_radius_squared = ii * ii + jj * jj;
}
}
if (nearest_radius_squared < (radius + 1) * (radius + 1))
break;
}
/*
* Unlinearize
*/
pixel unlinearized(exp->unlinearize((value - minval_pixel)
/ (maxval - minval)));
unlinearized = unlinearized.clamp();
p->red = (Quantum) ale_real_to_int(unlinearized[0], MaxRGB);
p->green = (Quantum) ale_real_to_int(unlinearized[1], MaxRGB);
p->blue = (Quantum) ale_real_to_int(unlinearized[2], MaxRGB);
p++;
}
if (!SyncImagePixels(mi))
break;
}
if (!WriteImage(image_info, mi)) {
/*
* Perhaps file type was unknown? Set to PNM by default.
*/
strncpy(mi->magick, "PNM", MaxTextExtent);
strncpy(image_info->magick, "PNM", MaxTextExtent);
if (!WriteImage(image_info, mi)) {
fprintf(stderr, "\n\n");
CatchException(&mi->exception);
fprintf(stderr, "\n");
exit(1);
}
}
DestroyImage(mi);
DestroyImageInfo(image_info);
#else
write_ppm(filename, im, exp, mcv, ppm_type == 2, rezero, exposure_scale || exp_scale_override,
nn_defined_radius);
#endif
}
static void output(const image *i) {
assert (file_count > 0);
write_image(output_name(), i, output_exposure);
}
static void vise_write(const char *p, const char *s, const image *i) {
static int count = 0;
int length = strlen(p) + strlen(s) + 8;
char *output_string = (char *) malloc(length * sizeof(char));
snprintf(output_string, length, "%s%08d%s", p, count, s);
write_image(output_string, i, output_exposure);
count++;
}
static exposure &exp(int n) {
return *input_exposure[n];
}
static const exposure &const_exp(int n) {
return *input_exposure[n];
}
static exposure &exp() {
return *output_exposure;
}
static void exp_scale() {
exposure_scale = 1;
}
static void exp_noscale() {
exposure_scale = 0;
}
static const exposure &const_exp() {
return *output_exposure;
}
static const unsigned int bayer(unsigned int n) {
if (bayer_specific[n] == IMAGE_BAYER_DEFAULT)
return bayer_default;
else
return bayer_specific[n];
}
static const image *open(unsigned int n) {
assert (n < file_count);
assert (!files_open[n]);
files_open[n] = 1;
if (latest_close_num >= 0 && n == (unsigned int) latest_close_num) {
latest_close_num = -1;
return images[n];
}
if (n < cache_count)
return images[n];
ui::get()->loading_file();
image *i = read_image(filenames[n], input_exposure[n], "file", bayer(n), (n == 0));
images[n] = i;
return images[n];
}
static void open_all() {
for (unsigned int n = 0; n < file_count; n++)
open(n);
}
static const image *get_open(unsigned int n) {
assert (files_open[n]);
return images[n];
}
static image *copy(unsigned int n, const char *name) {
assert (n < file_count);
if (files_open[n])
return images[n]->clone(name);
else {
image *i = read_image(filenames[n], input_exposure[n], name, bayer(n), (n == 0));
return i;
}
}
static void close(unsigned int image) {
assert (image < file_count);
assert (files_open[image]);
files_open[image] = 0;
if (image < cache_count)
return;
if (image == cache_count) {
double image_size = ((double) images[image]->storage_size()) / pow(2, 20);
if (image_size + cache_size < cache_size_max) {
cache_size += image_size;
cache_count++;
ui::get()->cache(cache_size, cache_size_max);
return;
} else {
ui::get()->cache_status(0);
}
}
if (latest_close_num >= 0)
delete images[latest_close_num];
latest_close_num = image;
}
static void close_all() {
for (unsigned int n = 0; n < file_count; n++)
close(n);
}
};
#endif

97
d2/image_weighted_avg.h Normal file
View File

@@ -0,0 +1,97 @@
// Copyright 2002, 2003, 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
*/
/*
* image_weighted_avg.h: Image representing a weighted average of inputs.
*/
#ifndef __image_weighted_avg_h__
#define __image_weighted_avg_h__
#include "image_ale_real.h"
#include "exposure/exposure.h"
#include "point.h"
#include "image.h"
class image_weighted_avg : public image {
private:
void trigger(pixel multiplier) {
assert(0);
}
public:
image_weighted_avg (unsigned int dimy, unsigned int dimx, unsigned int
depth, const char *name = "anonymous")
: image(dimy, dimx, depth, name, NULL) {
}
virtual ~image_weighted_avg() {
}
void set_pixel(unsigned int y, unsigned int x, spixel p) {
assert(0);
}
spixel get_pixel(unsigned int y, unsigned int x) const {
assert(0);
return spixel(0, 0, 0);
}
void set_chan(unsigned int y, unsigned int x, unsigned int k, ale_sreal c) {
assert(0);
}
ale_sreal get_chan(unsigned int y, unsigned int x, unsigned int k) const {
assert(0);
return 0;
}
/*
* Make a new image suitable for receiving scaled values.
*/
virtual image *scale_generator(int height, int width, int depth, const char *name) const {
return new_image_ale_real(height, width, depth, name, _exp);
}
/*
* Pre-transformation check for whether an area should be skipped.
* Takes image weights as an argument.
*/
virtual int accumulate_norender(int i, int j) = 0;
/*
* Accumulate pixels
*/
virtual void accumulate(int i, int j, int f, pixel new_value, pixel new_weight) = 0;
/*
* Get color map
*/
virtual image *get_colors() = 0;
/*
* Get weight map
*/
virtual image *get_weights() = 0;
};
#endif

248
d2/image_weighted_median.h Normal file
View File

@@ -0,0 +1,248 @@
// Copyright 2002, 2003, 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
*/
/*
* image_weighted_median.h: Image representing a weighted median of inputs.
*/
#ifndef __image_weighted_median_h__
#define __image_weighted_median_h__
#include "exposure/exposure.h"
#include "point.h"
#include "image.h"
class image_weighted_median : public image_weighted_avg {
private:
/*
* Array 'colors' stores image colors, sorted by intensity for each
* channel at each pixel location.
*
* Array 'weights' stores the weights associated with each color, where
* the weights are represented cumulatively, so that for weights and
* intensities:
*
* Color: 1 2 3 6 7
* Weight: 2 2 1 1 1
*
* The (cumulative) representation would be:
*
* Color: 1 2 3 6 7
* Weight: 2 4 5 6 7
*
* XXX: This storage approach may have poor cache characteristics.
* It might be better to localize elements having identical spatial
* coordinates.
*/
image **colors;
image **weights;
unsigned int capacity;
public:
image_weighted_median (unsigned int dimy, unsigned int dimx, unsigned int
depth, int capacity = -1, const char *name = "anonymous")
: image_weighted_avg(dimy, dimx, depth, name) {
if (capacity == -1) {
this->capacity = image_rw::count();
} else if (capacity >= 0) {
this->capacity = (unsigned int) capacity;
} else
assert(0);
colors = (image **) malloc(this->capacity * sizeof(image *));
weights = (image **) malloc(this->capacity * sizeof(image *));
assert(colors);
assert(weights);
if (!colors || !weights) {
fprintf(stderr, "Could not allocate memory for image data.\n");
exit(1);
}
for (unsigned int f = 0; f < this->capacity; f++) {
colors[f] = new_image_ale_real(dimy, dimx, depth);
weights[f] = new_image_ale_real(dimy, dimx, depth);
assert(colors[f]);
assert(weights[f]);
if (!colors[f] || !weights[f]) {
fprintf(stderr, "Could not allocate memory for image data.\n");
exit(1);
}
}
}
virtual ~image_weighted_median() {
for (unsigned int f = 0; f < capacity; f++) {
delete colors[f];
delete weights[f];
}
free(colors);
free(weights);
}
/*
* Extend the image area to the top, bottom, left, and right,
* initializing the new image areas with black pixels. Negative values
* shrink the image.
*/
image *_extend(int top, int bottom, int left, int right) {
for (unsigned int f = 0; f < capacity; f++) {
extend(&colors[f], top, bottom, left, right);
extend(&weights[f], top, bottom, left, right);
}
_dimx = colors[0]->width();
_dimy = colors[0]->height();
_offset = colors[0]->offset();
return NULL;
}
int accumulate_norender(int i, int j) {
return 0;
}
/*
* Perform insertion sort on the arrays, where sort is by color.
*
* XXX: This does a poor job of handling multiple contributions from
* the same frame, especially when the number of frames is 1.
*/
void accumulate(int i, int j, int f, pixel new_value, pixel new_weight) {
for (unsigned int k = 0; k < 3; k++) {
if (fabs(new_weight[k]) > ale_real_weight_floor)
new_value[k] /= new_weight[k];
else
continue;
/*
* XXX: This initialization should not be necessary.
*/
if (f == 0)
for (unsigned int ff = 0; ff < capacity; ff++)
weights[ff]->set_chan(i, j, k, 0);
assert (finite(new_weight[k]));
if (new_weight[k] <= 0)
continue;
for (unsigned int ff = 0; ff < capacity; ff++) {
assert (ff <= (unsigned int) f);
if (ff == capacity - 1) {
colors[ff]->set_chan(i, j, k, new_value[k]);
weights[ff]->set_chan(i, j, k,
weights[ff]->get_chan(i, j, k) + new_weight[k]);
break;
}
if ((ff == 0 && weights[ff]->get_chan(i, j, k) == 0)
|| (ff > 0 && weights[ff]->get_chan(i, j, k) == weights[ff - 1]->get_chan(i, j, k))) {
colors[ff]->set_chan(i, j, k, new_value[k]);
for (unsigned int fff = ff; fff < capacity; fff++)
weights[fff]->set_chan(i, j, k,
weights[fff]->get_chan(i, j, k) + new_weight[k]);
break;
}
if (colors[ff]->get_chan(i, j, k) == (ale_sreal) new_value[k]) {
for (unsigned int fff = ff; fff < capacity; fff++)
weights[fff]->set_chan(i, j, k,
weights[fff]->get_chan(i, j, k) + new_weight[k]);
break;
}
if (colors[ff]->get_chan(i, j, k) > (ale_sreal) new_value[k]) {
for (unsigned int fff = capacity - 1; fff > ff; fff--) {
weights[fff]->set_chan(i, j, k, weights[fff - 1]->get_pixel(i, j)[k] + new_weight[k]);
colors[fff]->set_chan(i, j, k, colors[fff - 1]->get_pixel(i, j)[k]);
}
colors[ff]->set_chan(i, j, k, new_value[k]);
weights[ff]->set_chan(i, j, k, new_weight[k]);
if (ff > 0)
weights[ff]->set_chan(i, j, k,
weights[ff]->get_chan(i, j, k)
+ weights[ff - 1]->get_chan(i, j, k));
break;
}
}
}
}
/*
* XXX: This is inefficient in cases where only one channel is desired.
*/
spixel get_pixel(unsigned int y, unsigned int x) const {
pixel result;
for (int k = 0; k < 3; k++) {
ale_real midpoint = weights[capacity - 1]->get_chan(y, x, k) / 2;
if (midpoint == 0)
return pixel::zero();
/*
* Binary search.
*/
unsigned int l = 0;
unsigned int h = capacity - 1;
unsigned int m = h / 2;
while (h > l + 1) {
if ((ale_real) weights[m]->get_chan(y, x, k) < midpoint)
l = m;
else
h = m;
m = (h + l) / 2;
}
if ((ale_real) weights[l]->get_chan(y, x, k) < midpoint)
l = h;
if ((ale_real) weights[l]->get_chan(y, x, k) > midpoint)
result[k] = colors[l]->get_chan(y, x, k);
else if ((ale_real) weights[l]->get_chan(y, x, k) == midpoint)
result[k] = (colors[l]->get_chan(y, x, k)
+ colors[l + 1]->get_chan(y, x, k)) / 2;
else
assert(0);
}
return result;
}
image *get_weights() {
return weights[capacity - 1];
}
image *get_colors() {
return this;
}
};
#endif

205
d2/image_weighted_simple.h Normal file
View File

@@ -0,0 +1,205 @@
// Copyright 2002, 2003, 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
*/
/*
* image_weighted_simple.h: Image representing a weighted average of inputs.
* Covers simple cases that require space constant with frame count.
*/
#ifndef __image_weighted_simple_h__
#define __image_weighted_simple_h__
#include "image_weighted_avg.h"
#include "render/invariant.h"
class image_weighted_simple : public image_weighted_avg {
private:
invariant *inv;
image *colors;
image *weights;
public:
image_weighted_simple (unsigned int dimy, unsigned int dimx, unsigned int
depth, invariant *inv, const char *name = "anonymous")
: image_weighted_avg(dimy, dimx, depth, name) {
colors = new_image_ale_real(dimy, dimx, depth);
weights = new_image_ale_real(dimy, dimx, depth);
this->inv = inv;
}
virtual ~image_weighted_simple() {
delete colors;
delete weights;
}
/*
* Extend the image area to the top, bottom, left, and right,
* initializing the new image areas with black pixels. Negative values
* shrink the image.
*/
image *_extend(int top, int bottom, int left, int right) {
extend(&colors, top, bottom, left, right);
extend(&weights, top, bottom, left, right);
_dimx = colors->width();
_dimy = colors->height();
_offset = colors->offset();
return NULL;
}
/*
* Pre-transformation check for whether an area should be skipped.
* Takes image weights as an argument.
*/
int accumulate_norender(int i, int j) {
/*
* Initial value
*/
if (inv->is_first() && weights->get_chan(i, j, 0) != 0)
return 1;
/*
* Weight limit satisfied
*/
if (inv->is_avgf()
&& weights->get_chan(i, j, 0) > inv->get_param()
&& weights->get_chan(i, j, 1) > inv->get_param()
&& weights->get_chan(i, j, 2) > inv->get_param())
return 1;
return 0;
}
/*
* Accumulate pixels
*/
void accumulate(int i, int j, int f, pixel new_value, pixel new_weight) {
/*
* Perform operations separately for each channel
*/
for (unsigned int k = 0; k < 3; k++) {
/*
* Cases independent of the old pixel value and weight
* for which the update can be ignored.
*/
if (!inv->is_avgx()
&& new_weight[k] < render::get_wt())
continue;
/*
* Cases independent of the old pixel value and weight for which
* previous pixel values can be ignored.
*/
if (inv->is_last() && new_weight[k] >= render::get_wt()) {
colors->set_chan(i, j, k, new_value[k]);
weights->set_chan(i, j, k, new_weight[k]);
continue;
}
/*
* Obtain the old pixel weight.
*/
ale_real old_weight = weights->get_chan(i, j, k);
/*
* Cases independent of the old pixel value for which the
* update can be ignored.
*/
if (old_weight >= render::get_wt()
&& inv->is_first())
continue;
if (inv->is_avgf()
&& old_weight >= inv->get_param())
continue;
/*
* Cases independent of the old pixel value for which previous
* pixel values can be ignored.
*/
if (old_weight == 0
|| (old_weight < render::get_wt()
&& !inv->is_avgx())) {
weights->set_chan(i, j, k, new_weight[k]);
colors ->set_chan(i, j, k, new_value[k]);
continue;
}
if (inv->is_max()) {
/*
* Cases in which the old pixel value can be ignored
*/
if (new_value[k] * (ale_real) weights->get_chan(i, j, k)
> (ale_real) colors->get_chan(i, j, k) * new_weight[k]) {
weights->set_chan(i, j, k, new_weight[k]);
colors-> set_chan(i, j, k, new_value[k]);
}
continue;
} else if (inv->is_min()) {
/*
* Cases in which the old pixel value can be ignored
*/
if (new_value[k] * (ale_real) weights->get_chan(i, j, k)
< (ale_real) colors->get_chan(i, j, k) * new_weight[k]) {
weights->set_chan(i, j, k, new_weight[k]);
colors-> set_chan(i, j, k, new_value[k]);
}
continue;
}
/*
* Update weight and color values.
*/
weights->set_chan(i, j, k, weights->get_chan(i, j, k) + new_weight[k]);
colors->set_chan(i, j, k, colors->get_chan(i, j, k) + new_value[k]);
}
}
spixel get_pixel(unsigned int y, unsigned int x) const {
return (pixel) colors->get_pixel(y, x) / (pixel) weights->get_pixel(y, x);
}
image *get_weights() {
return weights;
}
image *get_colors() {
assert(0);
return colors;
}
};
#endif

246
d2/image_zero.h Normal file
View File

@@ -0,0 +1,246 @@
// Copyright 2002, 2003, 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
*/
/*
* zero_image.h: Image that is zero everywhere.
*/
#ifndef __image_zero_h__
#define __image_zero_h__
#include "point.h"
#include "pixel.h"
#include "exposure/exposure.h"
class image_zero : public image_weighted_avg {
public:
spixel get_pixel(unsigned int y, unsigned int x) const {
return pixel::zero();
}
void set_pixel(unsigned int y, unsigned int x, spixel p) {
assert(0);
}
void set_chan(unsigned int y, unsigned int x, unsigned int k, ale_sreal c) {
assert(0);
}
ale_sreal get_chan(unsigned int y, unsigned int x, unsigned int k) const {
return 0;
}
ale_real maxval() const {
return 0;
}
ale_real minval() const {
return 0;
}
/*
* Get a color value at a given position using bilinear interpolation between the
* four nearest pixels. Result values:
*
* result[0] == pixel value
* result[1] == pixel confidence
*/
void get_bl(point x, pixel result[2]) const {
result[0] = pixel::zero();
result[1] = pixel::zero();
}
pixel get_bl(point x) const {
pixel result[2];
get_bl(x, result);
return result[0];
}
pixel get_scaled_bl(point x, ale_pos f) const {
return pixel::zero();
}
/*
* Make a new image suitable for receiving scaled values.
*/
virtual image *scale_generator(int height, int width, int depth, const char *name) const {
image *is = new image_zero(height, width, depth, name);
assert(is);
return is;
}
/*
* Return an image scaled by some factor >= 1.0
*/
image *scale(ale_pos f, const char *name) const {
image *is = new image_zero(
(int) floor(height() * f),
(int) floor(width() * f), depth());
assert(is);
return is;
}
/*
* Scale by half. We use the following filter:
*
* 1/16 1/8 1/16
* 1/8 1/4 1/8
* 1/16 1/8 1/16
*
* At the edges, these values are normalized so that the sum of
* contributing pixels is 1.
*/
image *scale_by_half(const char *name) const {
ale_pos f = 0.5;
image *result = new image_zero(
(int) floor(height() * f),
(int) floor(width() * f), depth());
assert(result);
return result;
}
/*
* Scale an image definition array by 1/2.
*
* ALE considers an image definition array as a special kind of image
* weight array (typedefs of which should appear below the definition
* of this class). ALE uses nonzero pixel values to mean 'defined' and
* zero values to mean 'undefined'. Through this interpretation, the
* image weight array implementation that ALE uses allows image weight
* arrays to also serve as image definition arrays.
*
* Whereas scaling of image weight arrays is not generally obvious in
* either purpose or method, ALE requires that image definition arrays
* be scalable, and the method we implement here is a fairly obvious
* one. In particular, if any source pixel contributing to the value of
* a scaled target pixel has an undefined value, then the scaled target
* pixel is undefined (zero). Otherwise, it is defined (non-zero).
*
* Since there are many possible ways of implementing this function, we
* choose an easy way and simply multiply the numerical values of the
* source pixels to obtain the value of the scaled target pixel.
*
* XXX: we consider all pixels within a 9-pixel square to contribute.
* There are other approaches. For example, edge pixels could be
* considered to have six contributing pixels and corner pixels four
* contributing pixels. To use this convention, change the ': 0' text
* in the code below to ': 1'.
*/
image *defined_scale_by_half(const char *name) const {
ale_pos f = 0.5;
image *result = new image_zero(
(int) floor(height() * f),
(int) floor(width() * f), depth());
assert(result);
return result;
}
/*
* Extend the image area to the top, bottom, left, and right,
* initializing the new image areas with black pixels.
*/
virtual image *_extend(int top, int bottom, int left, int right) {
_dimy += top + bottom;
_dimx += left + right;
_offset[0] -= top;
_offset[1] -= left;
return NULL;
}
/*
* Clone
*/
image *clone(const char *name) const {
return new image_zero(_dimy, _dimx, _depth, name);
}
/*
* Calculate the average (mean) clamped magnitude of a channel across
* all pixels in an image. The magnitude is clamped to the range of
* real inputs.
*/
ale_real avg_channel_clamped_magnitude(unsigned int k) const {
return 0;
}
pixel avg_channel_clamped_magnitude() const {
return pixel::zero();
}
/*
* Calculate the average (mean) magnitude of a channel across all
* pixels in an image.
*/
ale_real avg_channel_magnitude(unsigned int k) const {
return 0;
}
pixel avg_channel_magnitude() const {
return pixel::zero();
}
/*
* Calculate the average (mean) magnitude of a pixel (where magnitude
* is defined as the mean of the channel values).
*/
ale_real avg_pixel_magnitude() const {
return 0;
}
image_zero(unsigned int dimy, unsigned int dimx, unsigned int depth,
const char *name = "anonymous") : image_weighted_avg(dimy, dimx, depth, name) {
}
int accumulate_norender(int i, int j) {
return 1;
}
void accumulate(int i, int j, int f, pixel new_value, pixel new_weight) {
assert(0);
}
image *get_colors() {
return this;
}
image *get_weights() {
return this;
}
};
#endif

277
d2/pixel.h Normal file
View File

@@ -0,0 +1,277 @@
// 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
*/
#ifndef __pixel_h__
#define __pixel_h__
/*
* Structure to describe a pixel
*/
class pixel {
private:
ale_real x[3];
public:
pixel() {
x[0] = 0;
x[1] = 0;
x[2] = 0;
}
pixel(ale_real x0, ale_real x1, ale_real x2) {
x[0] = x0;
x[1] = x1;
x[2] = x2;
}
pixel(const pixel &p) {
x[0] = p[0];
x[1] = p[1];
x[2] = p[2];
}
pixel &operator=(const pixel &p) {
x[0] = p[0];
x[1] = p[1];
x[2] = p[2];
return (*this);
}
// Due to automatic typecasts and automatic int <==> ale_real *
// conversions, this can cause some really weird bugs.
//
// pixel(ale_real *_x) {
// x[0] = _x[0];
// x[1] = _x[1];
// x[2] = _x[2];
// }
const ale_real &operator[](unsigned int i) const {
#if 0
/*
* This may be expensive.
*/
assert (i < 3);
#endif
return x[i];
}
ale_real &operator[](unsigned int i) {
#if 0
/*
* This may be expensive.
*/
assert (i < 3);
#endif
return x[i];
}
pixel operator+(pixel p) const {
return pixel(p[0] + x[0], p[1] + x[1], p[2] + x[2]);
}
pixel operator-(pixel p) const {
return pixel(x[0] - p[0], x[1] - p[1], x[2] - p[2]);
}
pixel operator-() const {
return pixel(-x[0], -x[1], -x[2]);
}
pixel operator/(pixel p) const {
return pixel(x[0] / p[0], x[1] / p[1], x[2] / p[2]);
}
pixel operator/(ale_real d) const {
return pixel(x[0] / d, x[1] / d, x[2] / d);
}
pixel mult(pixel p) const {
return pixel(x[0] * p[0], x[1] * p[1], x[2] * p[2]);
}
pixel mult(ale_real d) const {
return pixel(x[0] * d, x[1] * d, x[2] * d);
}
pixel operator+=(pixel p) {
return pixel(x[0] += p[0], x[1] += p[1], x[2] += p[2]);
}
pixel operator*=(pixel p) {
return pixel(x[0] *= p[0], x[1] *= p[1], x[2] *= p[2]);
}
pixel operator*=(ale_real d) {
return pixel(x[0] *= d, x[1] *= d, x[2] *= d);
}
pixel operator/=(pixel p) {
return pixel(x[0] /= p[0], x[1] /= p[1], x[2] /= p[2]);
}
pixel operator/=(ale_real d) {
return pixel(x[0] /= d, x[1] /= d, x[2] /= d);
}
pixel clamp() const {
pixel result;
for (int i = 0; i < 3; i++)
if (x[i] > 1.0)
result[i] = 1.0;
else if (x[i] < 0.0)
result[i] = 0.0;
else
result[i] = x[i];
return result;
}
pixel abs() {
return pixel(fabs(x[0]), fabs(x[1]), fabs(x[2]));
}
ale_real normsq() {
return x[0] * x[0] + x[1] * x[1] + x[2] * x[2];
}
ale_real norm() {
return sqrt(normsq());
}
ale_real lnorm() {
return x[0] + x[1] + x[2];
}
ale_real maxabs_norm() {
ale_real m = fabs(x[0]);
if (fabs(x[1]) > m)
m = fabs(x[1]);
if (fabs(x[2]) > m)
m = fabs(x[2]);
return m;
}
ale_real minabs_norm() {
ale_real m = fabs(x[0]);
if (fabs(x[1]) < m)
m = fabs(x[1]);
if (fabs(x[2]) < m)
m = fabs(x[2]);
return m;
}
ale_real min_norm() const {
ale_real m = x[0];
if (x[1] < m)
m = x[1];
if (x[2] < m)
m = x[2];
return m;
}
ale_real max_norm() {
ale_real m = x[0];
if (x[1] > m)
m = x[1];
if (x[2] > m)
m = x[2];
return m;
}
static pixel zero() {
return pixel(0, 0, 0);
}
static pixel one() {
return pixel(1, 1, 1);
}
int operator==(const pixel &p) {
return x[0] == p[0]
&& x[1] == p[1]
&& x[2] == p[2];
}
int operator!=(const pixel &p) {
return !operator==(p);
}
int finite() {
return ::finite(x[0]) && ::finite(x[1]) && ::finite(x[2]);
}
static pixel undefined() {
ale_real zero = 0;
return pixel(zero / zero, zero / zero, zero / zero);
}
};
inline pixel operator*(const pixel &p, const pixel &q) {
return p.mult(q);
}
template<typename T>
inline pixel operator*(T d, const pixel &p) {
return p.mult(d);
}
template<typename T>
inline pixel operator*(const pixel &p, T d) {
return p.mult(d);
}
inline std::ostream &operator<<(std::ostream &o, const pixel &p) {
o << "[" << (double) p[0] << " " << (double) p[1] << " " << (double) p[2] << "]";
return o;
}
template<typename T>
inline pixel ppow(pixel p, T d) {
return pixel(
pow(p[0], d),
pow(p[1], d),
pow(p[2], d));
}
inline pixel pexp(pixel p) {
return pixel(
exp((double) p[0]),
exp((double) p[1]),
exp((double) p[2]));
}
inline pixel psqrt(pixel p) {
return pixel(
sqrt(p[0]),
sqrt(p[1]),
sqrt(p[2]));
}
#endif

158
d2/pixel_accum.h Normal file
View File

@@ -0,0 +1,158 @@
// 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
*/
#ifndef __pixel_accum_h__
#define __pixel_accum_h__
#include "pixel.h"
/*
* Structure to accumulate values over many pixels.
*/
class pixel_accum {
private:
ale_accum x[3];
public:
pixel_accum() {
x[0] = 0;
x[1] = 0;
x[2] = 0;
}
pixel_accum(ale_accum x0, ale_accum x1, ale_accum x2) {
x[0] = x0;
x[1] = x1;
x[2] = x2;
}
pixel_accum(pixel p) {
x[0] = p[0];
x[1] = p[1];
x[2] = p[2];
}
operator pixel() {
pixel result;
result[0] = x[0];
result[1] = x[1];
result[2] = x[2];
return result;
}
// Due to automatic typecasts and automatic int <==> ale_accum *
// conversions, this can cause some really weird bugs.
//
// pixel_accum(ale_accum *_x) {
// x[0] = _x[0];
// x[1] = _x[1];
// x[2] = _x[2];
// }
const ale_accum &operator[](int i) const {
assert (i >= 0);
assert (i < 3);
return x[i];
}
ale_accum &operator[](int i) {
assert (i >= 0);
assert (i < 3);
return x[i];
}
pixel_accum operator+(pixel_accum p) const {
return pixel_accum(p[0] + x[0], p[1] + x[1], p[2] + x[2]);
}
pixel_accum operator-(pixel_accum p) const {
return pixel_accum(x[0] - p[0], x[1] - p[1], x[2] - p[2]);
}
pixel_accum operator/(pixel_accum p) const {
return pixel_accum(x[0] / p[0], x[1] / p[1], x[2] / p[2]);
}
pixel_accum operator/(ale_accum d) const {
return pixel_accum(x[0] / d, x[1] / d, x[2] / d);
}
pixel_accum operator*(pixel_accum p) const {
return pixel_accum(x[0] * p[0], x[1] * p[1], x[2] * p[2]);
}
pixel_accum operator*(ale_accum d) const {
return pixel_accum(x[0] * d, x[1] * d, x[2] * d);
}
pixel_accum operator+=(pixel_accum p) {
return pixel_accum(x[0] += p[0], x[1] += p[1], x[2] += p[2]);
}
pixel_accum operator*=(pixel_accum p) {
return pixel_accum(x[0] *= p[0], x[1] *= p[1], x[2] *= p[2]);
}
pixel_accum operator*=(ale_accum d) {
return pixel_accum(x[0] *= d, x[1] *= d, x[2] *= d);
}
pixel_accum operator/=(pixel_accum p) {
return pixel_accum(x[0] /= p[0], x[1] /= p[1], x[2] /= p[2]);
}
pixel_accum operator/=(ale_accum d) {
return pixel_accum(x[0] /= d, x[1] /= d, x[2] /= d);
}
};
inline pixel_accum operator*(float d, const pixel_accum &p) {
return p * d;
}
inline pixel_accum operator*(double d, const pixel_accum &p) {
return p * d;
}
inline std::ostream &operator<<(std::ostream &o, const pixel_accum &p) {
o << "[" << (double) p[0] << " " << (double) p[1] << " " << (double) p[2] << "]";
return o;
}
inline pixel_accum ppow(pixel_accum p, float d) {
return pixel_accum(
pow((ale_accum) p[0], d),
pow((ale_accum) p[1], d),
pow((ale_accum) p[2], d));
}
inline pixel_accum ppow(pixel_accum p, double d) {
return pixel_accum(
pow((ale_accum) p[0], d),
pow((ale_accum) p[1], d),
pow((ale_accum) p[2], d));
}
#endif

215
d2/point.h Normal file
View File

@@ -0,0 +1,215 @@
// 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
*/
#ifndef __d2point_h__
#define __d2point_h__
/*
* Structure to describe a point
*/
class point {
private:
ale_pos x[2];
public:
point() {
}
point(ale_pos x0, ale_pos x1) {
x[0] = x0;
x[1] = x1;
}
const ale_pos &operator[](unsigned int i) const {
#if 0
/*
* This may be expensive.
*/
assert (i < 2);
#endif
return x[i];
}
ale_pos &operator[](unsigned int i) {
#if 0
/*
* This may be expensive.
*/
assert (i < 2);
#endif
return x[i];
}
point operator+(point p) const {
return point(p[0] + x[0], p[1] + x[1]);
}
point operator-(point p) const {
return point(x[0] - p[0], x[1] - p[1]);
}
point operator-() const {
return point(-x[0], -x[1]);
}
point operator+=(point p) {
(*this) = (*this) + p;
return *this;
}
point operator-=(point p) {
(*this) = (*this) - p;
return *this;
}
point mult(ale_pos d) const {
return point(x[0] * d, x[1] * d);
}
point operator*(point p) const {
/*
* element-wise multiplication
*/
return point(x[0] * p[0], x[1] * p[1]);
}
point operator *=(ale_pos d) {
(*this) = mult(d);
return *this;
}
point operator/(ale_pos d) const {
return point(x[0] / d, x[1] / d);
}
ale_pos normsq() const {
return x[0] * x[0] + x[1] * x[1];
}
ale_pos norm() const {
return sqrt(normsq());
}
ale_pos absmaxnorm() const {
ale_pos a = fabs(x[0]);
ale_pos b = fabs(x[1]);
return (a > b) ? a : b;
}
ale_pos lengthtosq(point p) const {
point diff = operator-(p);
return diff[0] * diff[0] + diff[1] * diff[1];
}
ale_pos lengthto(point p) const {
return sqrt(lengthtosq(p));
}
ale_pos dproduct(point p) const {
return (x[0] * p[0] + x[1] * p[1]);
}
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))
/ (2 * sqrt(to_p) * sqrt(to_q));
return acos(cos_of);
}
static point posinf() {
ale_pos a = +1;
ale_pos z = +0;
a = a / z;
assert (isinf(a) && a > 0);
return point(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 < 2; d++)
if (p[d] > x[d])
x[d] = p[d];
}
void accumulate_min(point p) {
for (int d = 0; d < 2; d++)
if (p[d] < x[d])
x[d] = p[d];
}
static point undefined() {
double a = 0;
point p(0, 0);
return p / a;
}
int defined() const {
return (!isnan(x[0])
&& !isnan(x[1]));
}
int finite() const {
return (::finite(x[0])
&& ::finite(x[1]));
}
static int defined(const point &p) {
return p.defined();
}
};
inline point operator*(const point &p, double d) {
return p.mult(d);
}
inline point operator*(double d, const point &p) {
return p.mult(d);
}
inline point operator*(float d, const point &p) {
return p.mult(d);
}
#endif

430
d2/ppm.h Normal file
View File

@@ -0,0 +1,430 @@
// 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
*/
/*
* ppm.h: Read and write PPM files.
*/
#ifndef __ppm_h__
#define __ppm_h__
#include "image_ale_real.h"
#include "image_bayer_ale_real.h"
#include "exposure/exposure.h"
/*
* Extended attributes
*/
struct extended_t {
int is_extended;
ale_real black_level;
ale_real aperture; /* 1 == f/1.0, 1.4 == f/1.4, etc. */
ale_real shutter; /* 1 == 1 sec, 0.5 == 1/2 sec, etc. */
ale_real gain; /* 1 == ISO 100, 2 == ISO 200, etc. */
extended_t() {
is_extended = 0;
black_level = 0;
aperture = 0;
shutter = 0;
gain = 0;
}
};
static inline void error_ppm(const char *filename) {
fprintf(stderr,
"\n\n*** '%s' doesn't look like a PPM file.\n"
"\n*** (To handle other file types, use a version of ALE with\n"
"*** ImageMagick support enabled.)\n\n",
filename);
exit(1);
}
static inline int digest_comment(FILE *f, const char *filename, extended_t *extended) {
int next = '#';
int value;
double fvalue, fvalue2;
while (next != '\n' && next != '\r' && next != EOF) {
while (next == ' ' || next == '\t' || next == '#') {
next = fgetc(f);
if (feof(f))
error_ppm(filename);
}
if (ungetc(next, f) == EOF) {
assert(0);
fprintf(stderr, "Unable to ungetc().");
exit(1);
}
fvalue2 = 1;
if (extended->is_extended && fscanf(f, "Black-level: %d", &value) == 1)
extended->black_level = value;
else if (extended->is_extended && fscanf(f, "ISO: %lf", &fvalue) == 1)
extended->gain = fvalue / 100;
else if (extended->is_extended && fscanf(f, "Gain: %lf", &fvalue) == 1)
extended->gain = fvalue;
else if (extended->is_extended && fscanf(f, "Aperture: %lf", &fvalue) == 1)
extended->aperture = fvalue;
else if (extended->is_extended && fscanf(f, "Shutter: %lf/%lf", &fvalue, &fvalue2) > 0)
extended->shutter = fvalue / fvalue2;
else if (next != '\n' && next != '\r' && next != EOF)
next = fgetc(f);
next = fgetc(f);
}
return next;
}
static inline void eat_comments(FILE *f, const char *filename, extended_t *extended) {
int next = ' ';
while (next == ' ' || next == '\n' || next == '\t' || next == '#' || next == '\r') {
next = fgetc(f);
if (next == '#')
next = digest_comment(f, filename, extended);
if (feof(f))
error_ppm(filename);
}
if (ungetc(next, f) == EOF) {
assert(0);
fprintf(stderr, "Unable to ungetc().");
exit(1);
}
}
static inline int is_eppm(const char *filename) {
char m1, m2, m3, m4;
int n;
extended_t extended;
FILE *f = fopen(filename, "rb");
if (f == NULL)
return 0;
/* Magic */
eat_comments(f, filename, &extended); /* XXX - should we eat comments here? */
n = fscanf(f, "%c%c%c%c", &m1, &m2, &m3, &m4);
fclose(f);
if (n != 4 || m1 != 'P' || (m2 != '6' && m2 != '3') || m3 != '#' || m4 != 'E')
return 0;
return 1;
}
static inline image *read_ppm(const char *filename, exposure *e, unsigned int bayer, int init_reference_gain = 0) {
unsigned int i, j, k;
image *im;
unsigned char m1, m2, val;
int m3, m4;
int ival;
int w, h, mcv;
int n;
struct extended_t extended;
FILE *f = fopen(filename, "rb");
if (f == NULL) {
fprintf(stderr, "\n\nUnable to open '%s'.\n\n", filename);
exit(1);
}
assert(f);
/* Magic */
eat_comments(f, filename, &extended); /* XXX - should we eat comments here? */
n = fscanf(f, "%c%c", &m1, &m2);
if (n != 2 || m1 != 'P' || (m2 != '6' && m2 != '3'))
error_ppm(filename);
assert(n == 2 && m1 == 'P' && (m2 == '6' || m2 == '3'));
/* Extended flag */
m3 = fgetc(f);
if (m3 == '#') {
m4 = fgetc(f);
if (m4 == 'E')
extended.is_extended = 1;
else while (m4 != EOF && m4 != '\n' && m4 != '\r')
m4 = fgetc(f);
} else if (ungetc(m3, f) == EOF) {
assert(0);
fprintf(stderr, "Unable to ungetc().");
exit(1);
}
/* Width */
eat_comments(f, filename, &extended);
n = fscanf(f, " %d", &w);
assert(n == 1);
if (n != 1)
error_ppm(filename);
/* Height */
eat_comments(f, filename, &extended);
n = fscanf(f, "%d", &h);
assert(n == 1);
if (n != 1)
error_ppm(filename);
/* Maximum component value */
eat_comments(f, filename, &extended);
n = fscanf(f, "%d", &mcv);
assert(n == 1);
assert(mcv <= 65535 || m2 == '3');
if (extended.black_level == 0) {
extended.black_level = e->get_black_level();
} else {
extended.black_level /= mcv;
}
if (n != 1 || (mcv > 65535 && m2 == '6'))
error_ppm(filename);
/* Make a new image */
if (bayer == IMAGE_BAYER_NONE)
im = new_image_ale_real(h, w, 3, "file", e);
else
im = new_image_bayer_ale_real(h, w, 3, bayer, "file", e);
assert (im);
/* Trailing whitespace */
if (fgetc(f) == EOF) {
assert(0);
error_ppm(filename);
}
/* Pixels */
for (i = 0; i < im->height(); i++)
for (j = 0; j < im->width(); j++) {
pixel p;
for (k = 0; k < im->depth(); k++) {
if (m2 == '6') {
/* Binary data */
n = fscanf(f, "%c", &val);
assert (n == 1);
if (n != 1)
error_ppm(filename);
ival = val;
if (mcv > 255) {
n = fscanf(f, "%c", &val);
assert(n == 1);
if (n != 1)
error_ppm(filename);
ival = (ival << 8) | val;
}
} else {
/* ASCII data */
eat_comments(f, filename, &extended);
n = fscanf(f, "%d", &ival);
assert (n == 1);
if (n != 1)
error_ppm(filename);
}
p[k] = ale_real_from_int(ival, mcv);
}
pixel p_linear = (e->linearize(p) - e->get_multiplier() * extended.black_level)
/ (1 - extended.black_level);
im->set_pixel(i, j, p_linear);
}
/* Handle exposure and gain */
if (extended.is_extended) {
if (extended.aperture != 0
|| extended.shutter != 0
|| extended.gain != 0) {
if (extended.aperture == 0)
extended.aperture = 1;
if (extended.shutter == 0)
extended.shutter = 1;
if (extended.gain == 0)
extended.gain = 1;
ale_real combined_gain = (1 / pow(extended.aperture, 2))
* extended.shutter
* extended.gain;
if (init_reference_gain)
exposure::set_gain_reference(combined_gain);
else
e->set_gain_multiplier(exposure::get_gain_reference()
/ combined_gain);
}
}
/* Done */
fclose(f);
return im;
}
static inline void write_ppm(const char *filename, const image *im, exposure *e,
unsigned int mcv, int plain, int rezero, int exposure_scale, double nn_defined_radius) {
unsigned int i, j, k;
FILE *f = fopen(filename, "wb");
if (f == NULL) {
fprintf(stderr, "\n\nUnable to open '%s'.\n\n", filename);
exit(1);
}
assert(f);
/*
* Output a plain (ASCII) or raw (binary) PPM file
*/
/* Magic */
if (plain)
fprintf(f, "P3 ");
else
fprintf(f, "P6 ");
/* Width */
fprintf(f, "%d ", im->width());
/* Height */
fprintf(f, "%d ", im->height());
/* Maximum component value */
fprintf(f, "%d\n", mcv);
/* Automatic exposure adjustment information */
ale_real maxval = 1;
ale_real minval = (rezero ? im->minval() : (ale_real) 0);
if (minval > 0)
minval = 0;
pixel minval_pixel(minval, minval, minval);
if (exposure_scale) {
ale_real new_maxval = im->maxval();
if (new_maxval > maxval)
maxval = new_maxval;
}
/* Pixels */
for (i = 0; i < im->height(); i++)
for (j = 0; j < im->width(); j++) {
pixel value = im->get_pixel(i, j);
/*
* Get nearest-neighbor defined values.
*
* XXX: While this implementation is correct, it is inefficient
* for large radii. A better implementation would search
* perimeters of squares of ever-increasing radius, tracking
* the best-so-far data until the square perimeter exceeded the
* best-so-far radius.
*/
for (k = 0; k < 3; k++)
if (isnan(value[k]))
for (int radius = 1; radius <= nn_defined_radius; radius++) {
double nearest_radius_squared = (radius + 1) * (radius + 1);
for (int ii = -radius; ii <= radius; ii++)
for (int jj = -radius; jj <= radius; jj++) {
if (!im->in_bounds(point(i + ii, j + jj)))
continue;
if (ii * ii + jj * jj < nearest_radius_squared
&& finite(im->get_pixel(i + ii, j + jj)[k])) {
value[k] = im->get_pixel(i + ii, j + jj)[k];
nearest_radius_squared = ii * ii + jj * jj;
}
}
if (nearest_radius_squared < (radius + 1) * (radius + 1))
break;
}
pixel exposure_adjust = (value - minval_pixel)
/ (maxval - minval);
pixel unlinearized = (e->unlinearize(exposure_adjust)).clamp();
for (k = 0; k < im->depth(); k++) {
uint16_t output_value = (uint16_t) ale_real_to_int(unlinearized[k], mcv);
if (plain) {
fprintf(f, "%d ", output_value);
} else {
if (mcv > 255)
fprintf(f, "%c", output_value >> 8);
fprintf(f, "%c", 0xff & output_value);
}
}
if (plain)
fprintf(f, "\n");
}
/* Done */
fclose(f);
}
#endif

34
d2/render.cc Normal file
View File

@@ -0,0 +1,34 @@
// 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
*/
#include "render.h"
/*
* See align.h for details on these variables.
*/
unsigned int render::rx_count;
exclusion *render::rx_parameters;
int render::rx_show;
render *render::directory[ACTIVE_RENDERER_COUNT];
int render::directory_length;
int render::extend;
ale_pos render::scale_factor;
ale_real render::wt = 1 / (ale_real) 10000;

376
d2/render.h Normal file
View File

@@ -0,0 +1,376 @@
// 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
*/
/*
* render.h: A superclass for all rendering classes.
*/
#ifndef __render_h__
#define __render_h__
#include "transformation.h"
#include "image.h"
#include "point.h"
#define ACTIVE_RENDERER_COUNT 30
/*
* Class render accepts messages synchronizing rendering steps through the
* methods sync(n) and sync(), and returns information about the currently
* rendered image via methods get_image() and get_defined(). This class is
* abstract, and must be subclassed to be instantiated.
*/
class render {
private:
static unsigned int rx_count;
static exclusion *rx_parameters;
static int rx_show;
static render *directory[ACTIVE_RENDERER_COUNT];
static int directory_length;
static int extend;
static ale_pos scale_factor;
static ale_real wt;
image **queue;
unsigned int queue_size;
int step_num;
int entry_number;
static int strpfix(const char *a, const char *b) {
return strncmp(a, b, strlen(a));
}
protected:
/*
* Constructor
*/
render() {
if (directory_length >= ACTIVE_RENDERER_COUNT) {
fprintf(stderr, "\n\n*** Too many renderers in d2::render::render() ***\n\n");
exit(1);
}
directory[directory_length] = this;
entry_number = directory_length;
directory_length++;
step_num = -1;
queue = NULL;
queue_size = 0;
}
/*
* Get extension state
*/
int is_extend() {
return extend;
}
/*
* Get the scale factor
*/
ale_pos get_scale_factor() {
return scale_factor;
}
/*
* Get the current step number
*/
int get_step() {
return step_num;
}
/*
* Perform the current rendering step.
*/
virtual void step() = 0;
public:
/*
* Check for render-coordinate excluded regions. (Applies an offset to
* spatial coordinates internally.)
*/
static int is_excluded_r(point offset, point p, int f) {
for (unsigned int param = 0; param < rx_count; param++)
if (rx_parameters[param].type == exclusion::RENDER
&& p[0] + offset[0] >= rx_parameters[param].x[0]
&& p[0] + offset[0] <= rx_parameters[param].x[1]
&& p[1] + offset[1] >= rx_parameters[param].x[2]
&& p[1] + offset[1] <= rx_parameters[param].x[3]
&& f >= rx_parameters[param].x[4]
&& f <= rx_parameters[param].x[5])
return 1;
return 0;
}
static int is_excluded_r(point offset, int i, int j, int f) {
for (unsigned int param = 0; param < rx_count; param++)
if (rx_parameters[param].type == exclusion::RENDER
&& i + offset[0] >= rx_parameters[param].x[0]
&& i + offset[0] <= rx_parameters[param].x[1]
&& j + offset[1] >= rx_parameters[param].x[2]
&& j + offset[1] <= rx_parameters[param].x[3]
&& f >= rx_parameters[param].x[4]
&& f <= rx_parameters[param].x[5])
return 1;
return 0;
}
int is_excluded_r(int i, int j, int f) {
return is_excluded_r(get_image()->offset(), i, j, f);
}
/*
* Check for frame-coordinate excluded regions.
*/
static int is_excluded_f(point p, int f) {
for (unsigned int param = 0; param < rx_count; param++)
if (rx_parameters[param].type == exclusion::FRAME
&& p[0] >= rx_parameters[param].x[0]
&& p[0] <= rx_parameters[param].x[1]
&& p[1] >= rx_parameters[param].x[2]
&& p[1] <= rx_parameters[param].x[3]
&& f >= rx_parameters[param].x[4]
&& f <= rx_parameters[param].x[5])
return 1;
return 0;
}
static int is_excluded_f(int i, int j, int f) {
for (unsigned int param = 0; param < rx_count; param++)
if (rx_parameters[param].type == exclusion::FRAME
&& i >= rx_parameters[param].x[0]
&& i <= rx_parameters[param].x[1]
&& j >= rx_parameters[param].x[2]
&& j <= rx_parameters[param].x[3]
&& f >= rx_parameters[param].x[4]
&& f <= rx_parameters[param].x[5])
return 1;
return 0;
}
static int render_count() {
return directory_length;
}
static render *render_num(int n) {
assert (n < directory_length);
return directory[n];
}
static void render_init(unsigned int _rx_count, exclusion *_rx_parameters,
int _rx_show, int _extend, ale_pos _scale_factor) {
rx_count = _rx_count;
rx_show = _rx_show;
extend = _extend;
scale_factor = _scale_factor;
rx_parameters = (exclusion *) malloc(rx_count * sizeof(exclusion));
for (unsigned int region = 0; region < rx_count; region++) {
rx_parameters[region] = _rx_parameters[region];
/*
* Scale spatial rendering coordinates
*/
if (rx_parameters[region].type == exclusion::RENDER)
for (int p = 0; p < 4; p++)
rx_parameters[region].x[p] *= scale_factor;
}
}
static void set_wt(ale_real _wt) {
wt = _wt;
}
static ale_real get_wt() {
return wt;
}
static int is_rx_show() {
return rx_show;
}
static unsigned int get_rx_count() {
return rx_count;
}
static const exclusion *get_rx_parameters() {
return rx_parameters;
}
/*
* Current rendering result.
*/
virtual const image *get_image() const = 0;
/*
* Result of rendering at the given frame.
*/
const image *get_image(unsigned int n) {
sync(n);
if (n == (unsigned int) step_num)
return get_image();
n = step_num - n - 1;
assert (n < queue_size);
return queue[n];
}
/*
* Extend the rendering queue.
*/
void extend_queue(unsigned int n) {
/*
* Increase the size of the queue, if necessary, to
* accommodate the given lag.
*/
if (n > queue_size) {
unsigned int new_size = n;
queue = (image **) realloc(queue, new_size * sizeof(image *));
assert(queue);
if (queue == NULL) {
fprintf(stderr, "\n\n*** VISE: Unable to allocate memory ***\n\n\n");
exit(1);
}
memset(queue + queue_size, 0, (new_size - queue_size) * sizeof(image *));
queue_size = new_size;
}
}
/*
* Definition map. Unit-depth image whose pixels are nonzero where
* the image is defined.
*/
virtual const image *get_defined() const = 0;
/*
* Sync.
*/
virtual void sync(int n) {
assert (step_num >= -1);
for (int i = step_num + 1; i <= n; i++) {
if (queue_size > 0 && step_num >= 0) {
/*
* Shift the current queue so that the new head remains at the
* zero index. There are more time-efficient ways to handle
* queues, but the benefits are not clear in this case.
*/
delete queue[queue_size - 1];
for (int i = queue_size - 1; i > 0; i--) {
queue[i] = queue[i - 1];
}
queue[0] = get_image()->clone("Render queue clone");
}
step_num++;
step();
}
}
/*
* Perform any final rendering steps. Return a non-zero value if
* anything changed.
*/
virtual int sync() {
return 0;
}
/*
* Set point rendering bounds, if possible.
*/
virtual void init_point_renderer(unsigned int h, unsigned int w, unsigned int d) {
assert(0);
fprintf(stderr, "Error: init_point_renderer() not supported by this renderer\n");
exit(1);
}
/*
* Point render.
*/
virtual void point_render(unsigned int i, unsigned int j, unsigned int f, transformation t) {
assert(0);
fprintf(stderr, "Error: point_render() not supported by this renderer\n");
exit(1);
}
/*
* Finish point rendering.
*/
virtual void finish_point_rendering() {
assert(0);
fprintf(stderr, "Error: finish_point_rendering() not supported by this renderer\n");
exit(1);
}
virtual ~render() {
directory[entry_number] = NULL;
}
int entry() {
return entry_number;
}
virtual void free_memory() = 0;
static void free_entry(int n) {
if (directory[n] != NULL) {
directory[n]->free_memory();
delete directory[n];
directory[n] = NULL;
}
}
static void free_all_memory() {
for (int i = 0; i < ACTIVE_RENDERER_COUNT; i++)
free_entry(i);
directory_length = 0;
}
static void reset() {
free_all_memory();
}
};
#endif

519
d2/render/combine.h Normal file
View File

@@ -0,0 +1,519 @@
// Copyright 2002, 2007 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
*/
/*
* combine.h: A renderer that combines two renderings.
*/
#ifndef __combine_h__
#define __combine_h__
#include "../transformation.h"
#include "../image.h"
#include "../point.h"
#include "incremental.h"
#include "../filter/filter.h"
/*
* Combine two renderings.
*
* Available data is taken from the PARTIAL rendering. When no data from
* the PARTIAL rendering is available, data from the DEFAULT rendering
* is substituted.
*/
class combine : public render {
private:
render *_default;
render *partial;
mutable image *output_image;
mutable image *defined_image;
int synced;
class refilter : public thread::decompose_domain {
combine *c;
const render *fine;
const render *coarse;
const filter::filter *f;
const image *fine_weight;
const image *fine_image;
const image *coarse_image;
const image *coarse_defined;
image *output_image;
/*
* Attempt to determine a distance by finding two nearby defined
* pixels, such that each pixel is in a 90-degree axis-aligned
* cone opposite the other.
*/
ale_pos find_nonzero_weight_distance(int i, int j, int k) {
assert (i >= 0);
assert (j >= 0);
assert (i < (int) coarse_defined->height());
assert (j < (int) coarse_defined->width());
assert (coarse_defined->get_chan(i, j, k) > 0);
ale_pos zero = +0.0;
ale_pos one = +1.0;
ale_pos nearest = one / zero;
assert (isinf(nearest) && nearest > 0);
int radius = 0;
int in_bounds = 1;
int coords[2];
while (radius < nearest && in_bounds) {
in_bounds = 0;
for (int ii = i - radius; ii <= i + radius; ii++)
for (int jj = j - radius; jj <= j + radius;
jj += ((abs(i - ii) == radius)
? 1
: radius * 2)) {
if (ii < 0
|| jj < 0
|| ii >= (int) coarse_defined->height()
|| jj >= (int) coarse_defined->width()
|| !(coarse_defined->get_chan(ii, jj, k) > 0))
continue;
in_bounds = 1;
if (!(fine_weight->get_chan(ii, jj, k) > 0))
continue;
ale_pos distance = sqrt( (ale_pos) ((i - ii) * (i - ii)
+ (j - jj) * (j - jj)));
if (distance < nearest) {
nearest = distance;
coords[0] = ii;
coords[1] = jj;
}
}
radius++;
}
if (isinf(nearest))
return nearest;
int cone_axis = 0;
int cone_dir = 1;
if (abs(coords[0] - i) < abs(coords[1] - j))
cone_axis = 1;
int orig_coords[2] = {i, j};
if (coords[cone_axis] - orig_coords[cone_axis] > 0)
cone_dir = -1;
nearest = one / zero;
assert (isinf(nearest) && nearest > 0);
radius = 1;
in_bounds = 1;
int coords2[2];
i = coords[0];
j = coords[1];
while (radius < nearest && in_bounds) {
in_bounds = 0;
coords2[cone_axis] = orig_coords[cone_axis] + radius * cone_dir;
for (coords2[1 - cone_axis] = orig_coords[1 - cone_axis] - radius;
coords2[1 - cone_axis] < orig_coords[1 - cone_axis] + radius;
coords2[1 - cone_axis]++) {
int ii = coords2[0];
int jj = coords2[1];
if (ii < 0
|| jj < 0
|| ii >= (int) coarse_defined->height()
|| jj >= (int) coarse_defined->width()
|| !(coarse_defined->get_chan(ii, jj, k) > 0))
continue;
in_bounds = 1;
if (!(fine_weight->get_chan(ii, jj, k) > 0))
continue;
ale_pos distance = sqrt( (ale_pos) ((i - ii) * (i - ii)
+ (j - jj) * (j - jj)));
if (distance < nearest)
nearest = distance;
}
radius++;
}
return nearest;
}
protected:
void subdomain_algorithm(unsigned int thread,
int i_min, int i_max, int j_min, int j_max) {
for (int i = i_min; i < i_max; i++)
for (int j = j_min; j < j_max; j++)
for (unsigned int k = 0; k < 3; k++){
if (!(coarse_defined->get_chan(i, j, k) > 0))
continue;
ale_pos filter_scale = 1;
ale_real filtered_weight;
ale_real filtered_value;
/*
* Attempt to set an initial filter scale based
* on the proximity of two nearby k-defined
* pixels.
*/
ale_pos n1 = find_nonzero_weight_distance(i, j, k);
if (!finite(n1)) {
output_image->set_chan(i, j, k, coarse_image->get_pixel(i, j)[k]);
continue;
}
filter_scale = n1;
do {
filtered_weight = 0;
filtered_value = 0;
/*
* lrintf() may be faster than ceil/floor() on some architectures.
* See render/psf/raster.h for more details.
*/
int support_extreme = (int) lrintf(f->support() * filter_scale);
assert (support_extreme >= 0);
for (int ii = -support_extreme;
ii < support_extreme; ii++)
for (int jj = -support_extreme;
jj < support_extreme; jj++) {
if (ii + i < 0
|| jj + j < 0
|| ii + i >= (int) fine_weight->height()
|| jj + j >= (int) fine_weight->width())
continue;
ale_real pw = fine_weight->get_chan(i + ii, j + jj, k);
if (!(pw > 0))
continue;
/*
* XXX: Set the weight to one
* for now, to prevent
* interference from certainty
* values calculated under
* different assumptions.
*/
pw = 1;
ale_real w = pw * f->response(point(ii / filter_scale,
jj / filter_scale));
ale_real v = fine_image->get_pixel(i + ii, j + jj)[k];
if (!finite(w) || !finite(v))
continue;
filtered_weight += w;
filtered_value += w * v;
}
if (filtered_weight < render::get_wt())
/* filter_scale += 1; */
filter_scale *= 2;
} while (filtered_weight < render::get_wt()
&& filter_scale < coarse_defined->width()
+ coarse_defined->height());
output_image->set_chan(i, j, k, filtered_value / filtered_weight);
}
}
public:
refilter(combine *_c,
const render *_fine,
const render *_coarse,
const filter::filter *_f,
const image *_fine_weight,
const image *_fine_image,
const image *_coarse_image,
const image *_coarse_defined,
image *_output_image) : decompose_domain(0, _coarse_defined->height(),
0, _coarse_defined->width()) {
c = _c;
fine = _fine;
coarse = _coarse;
f = _f;
fine_weight = _fine_weight;
fine_image = _fine_image;
coarse_image = _coarse_image;
coarse_defined = _coarse_defined;
output_image = _output_image;
}
};
const image *get_image_dynamic() const {
assert(typeid(*partial) == typeid(incremental));
if (typeid(*_default) != typeid(combine) || !synced) {
/*
* Degenerate case.
*/
output_image = _default->get_image()->clone("degenerate dynamic filter");
return output_image;
}
combine *c = (combine *)_default;
const render *fine = c->get_partial();
const render *coarse = c->get_default();
const filter::filter *f = ((incremental *)partial)->get_invariant()->ssfe()->
get_scaled_filter()->get_filter();
const image *fine_weight = fine->get_defined();
const image *fine_image = fine->get_image();
const image *coarse_image = coarse->get_image();
const image *coarse_defined = coarse->get_defined();
output_image = new_image_ale_real(coarse_defined->height(),
coarse_defined->width(), 3, NULL);
output_image->set_offset(coarse_defined->offset());
assert (coarse_defined->width() == fine_image->width());
assert (coarse_defined->height() == fine_image->height());
assert (coarse_defined->width() == fine_weight->width());
assert (coarse_defined->height() == fine_weight->height());
ui::get()->refilter_start();
refilter r(c, fine, coarse, f, fine_weight, fine_image, coarse_image,
coarse_defined, output_image);
r.run();
ui::get()->refilter_done();
return output_image;
}
public:
/*
* Constructor
*/
combine(render *_default, render *partial) {
this->_default = _default;
this->partial = partial;
this->output_image = NULL;
this->defined_image = NULL;
this->synced = 0;
}
virtual ~combine() {
if (output_image)
delete output_image;
if (defined_image)
delete defined_image;
}
/*
* Result of rendering.
*/
virtual const image *get_image() const {
if (output_image)
return output_image;
assert(typeid(*partial) != typeid(combine));
/*
* Dynamic filtering is handled separately.
*/
if (typeid(*partial) == typeid(incremental)
&& (((incremental *)partial)->get_invariant()->
ssfe()->get_scaled_filter()->is_dynamic()))
return get_image_dynamic();
const image *default_image = _default->get_image();
output_image = new_image_ale_real(default_image->height(),
default_image->width(), 3, NULL);
output_image->set_offset(default_image->offset());
const image *partial_image = partial->get_image();
const image *partial_weight = partial->get_defined();
assert (default_image->width() == partial_image->width());
assert (default_image->height() == partial_image->height());
for (unsigned int i = 0; i < default_image->height(); i++)
for (unsigned int j = 0; j < default_image->width(); j++)
output_image->set_pixel(i, j,
((ale_real) ((pixel) partial_weight->get_pixel(i, j)).min_norm()
>= render::get_wt())
? partial_image->get_pixel(i, j)
: default_image->get_pixel(i, j));
return output_image;
}
/*
* Definition map. Unit-depth image whose pixels are nonzero where
* the image is defined.
*/
virtual const image *get_defined() const {
unsigned int i, j, k;
if (defined_image)
return defined_image;
const image *partial_weight = partial->get_defined();
const image *default_weight = _default->get_defined();
assert (default_weight->width() == partial_weight->width());
assert (default_weight->height() == partial_weight->height());
defined_image = new_image_ale_real(default_weight->height(),
default_weight->width(), 3, NULL);
defined_image->set_offset(default_weight->offset());
for (i = 0; i < default_weight->height(); i++)
for (j = 0; j < default_weight->width(); j++)
for (k = 0; k < default_weight->depth(); k++)
defined_image->set_pixel(i, j,
((ale_real) ((pixel) partial_weight->get_pixel(i, j)).min_norm()
>= render::get_wt())
? partial_weight->get_pixel(i, j)
: default_weight->get_pixel(i, j));
return defined_image;
}
/*
* Perform rendering steps requiring no frames beyond frame N.
*/
virtual void sync(int n) {
render::sync(n);
if (output_image) {
delete output_image;
output_image = NULL;
}
if (defined_image) {
delete defined_image;
defined_image = NULL;
}
_default->sync(n);
partial->sync(n);
}
virtual void step() {
}
virtual int sync() {
if (output_image) {
delete output_image;
output_image = NULL;
}
if (defined_image) {
delete defined_image;
defined_image = NULL;
}
_default->sync();
partial->sync();
synced = 1;
return 1;
}
virtual void init_point_renderer(unsigned int h, unsigned int w, unsigned int d) {
_default->init_point_renderer(h, w, d);
partial->init_point_renderer(h, w, d);
output_image = new image_zero(h, w, d);
defined_image = new image_zero(h, w, d);
}
virtual void point_render(unsigned int i, unsigned int j, unsigned int f, transformation t) {
_default->point_render(i, j, f, t);
partial->point_render(i, j, f, t);
}
virtual void finish_point_rendering() {
_default->finish_point_rendering();
partial->finish_point_rendering();
delete defined_image;
delete output_image;
/*
* These will be generated upon a call to get_image() or
* get_defined().
*/
defined_image = NULL;
output_image = NULL;
}
const render *get_default() const {
return _default;
}
const render *get_partial() const {
return partial;
}
void free_memory() {
delete output_image;
delete defined_image;
output_image = NULL;
defined_image = NULL;
}
};
#endif

427
d2/render/incremental.h Normal file
View File

@@ -0,0 +1,427 @@
// Copyright 2002, 2004, 2007 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 __incremental_h__
#define __incremental_h__
#include "invariant.h"
#include "../render.h"
#include "../transformation.h"
#include "../image.h"
#include "../point.h"
/*
* Class for incremental renderers.
*/
class incremental : public render {
protected:
image_weighted_avg *accum_image;
invariant *inv;
/*
* Set extents of image and weight according to a new image to be
* merged. This function should remove only superfluous undefined
* areas.
*/
void set_extents_by_map(unsigned int frame_num, transformation t) {
assert (accum_image != NULL);
ale_pos extend_offset_i = accum_image->offset()[0];
ale_pos extend_offset_j = accum_image->offset()[1];
int extend_top = 0;
int extend_bottom = 0;
int extend_left = 0;
int extend_right = 0;
ale_pos zero = 0;
ale_pos infinity = 1 / zero;
assert (!finite(infinity));
assert (!isnan(infinity));
assert (infinity > 0);
point min, max;
min[0] = min[1] = infinity;
max[0] = max[1] = -infinity;
for (unsigned int i = 0; i < t.unscaled_height(); i++)
for (unsigned int j = 0; j < t.unscaled_width(); j++) {
if (is_excluded_f(i, j, frame_num))
continue;
point p = t.transform_unscaled(point(i, j));
if (is_excluded_r(accum_image->offset(), p, frame_num))
continue;
if (p[0] < min[0]) {
min[0] = p[0];
}
if (p[0] > max[0]) {
max[0] = p[0];
}
if (p[1] < min[1]) {
min[1] = p[1];
}
if (p[1] > max[1]) {
max[1] = p[1];
}
}
if (!finite(max[0])
|| !finite(max[1])
|| !finite(min[0])
|| !finite(min[1]))
return;
extend_top = (int) ceil(extend_offset_i - floor(min[0]));
extend_left = (int) ceil(extend_offset_j - floor(min[1]));
extend_bottom = (int) ceil(ceil(max[0]) - (ale_pos) (accum_image->height() - 1 + extend_offset_i));
extend_right = (int) ceil(ceil(max[1]) - (ale_pos) (accum_image->width() - 1 + extend_offset_j));
accum_image->_extend(extend_top, extend_bottom,
extend_left, extend_right);
}
void increase_extents_by_map(unsigned int frame_num, transformation t) {
assert (accum_image != NULL);
ale_pos extend_offset_i = accum_image->offset()[0];
ale_pos extend_offset_j = accum_image->offset()[1];
int extend_top = 0;
int extend_bottom = 0;
int extend_left = 0;
int extend_right = 0;
double zero = 0;
double infinity = 1 / zero;
assert (!finite(infinity));
assert (!isnan(infinity));
assert (infinity > 0);
point min, max;
min[0] = min[1] = infinity;
max[0] = max[1] = -infinity;
for (unsigned int i = 0; i < t.unscaled_height(); i++)
for (unsigned int j = 0; j < t.unscaled_width(); j++) {
if (is_excluded_f(i, j, frame_num))
continue;
point p = t.transform_unscaled(point(i, j));
if (is_excluded_r(point(0, 0), p, frame_num))
continue;
if (p[0] < min[0]) {
min[0] = p[0];
}
if (p[0] > max[0]) {
max[0] = p[0];
}
if (p[1] < min[1]) {
min[1] = p[1];
}
if (p[1] > max[1]) {
max[1] = p[1];
}
}
if (!finite(max[0])
|| !finite(max[1])
|| !finite(min[0])
|| !finite(min[1]))
return;
if (ceil(min[0]) < extend_offset_i)
extend_top = (int) ceil(extend_offset_i - floor(min[0]));
if (ceil(min[1]) < extend_offset_j)
extend_left = (int) ceil(extend_offset_j - floor(min[1]));
if (floor(max[0]) > accum_image->height() - 1 + extend_offset_i)
extend_bottom = (int) ceil(ceil(max[0]) - (ale_pos) (accum_image->height() - 1 + extend_offset_i));
if (floor(max[1]) > accum_image->width() - 1 + extend_offset_j)
extend_right = (int) ceil(ceil(max[1]) - (ale_pos) (accum_image->width() - 1 + extend_offset_j));
accum_image->_extend(extend_top, extend_bottom,
extend_left, extend_right);
}
/*
* Merge operation for a single pixel in the accumulated image.
*/
void _merge_pixel(int frame, const image *delta, transformation t, int i, int j, const filter::ssfe *_ssfe) {
if (_ssfe->ex_is_honored() && is_excluded_r(i, j, frame))
return;
if (accum_image->accumulate_norender(i, j))
return;
/*
* Pixel value to be merged, and the associated
* confidence
*/
pixel value, confidence;
if (exposure::get_confidence() != 0) {
_ssfe->filtered(i, j, frame, &value, &confidence, ((pixel) accum_image->get_pixel(i, j)), accum_image->get_weights()->get_pixel(i, j));
} else {
_ssfe->filtered(i, j, frame, &value, &confidence);
}
accum_image->accumulate(i, j, frame, value, confidence);
}
/*
* Merge part of a delta frame with part of the accumulated image using
* the specified transformation.
*/
class merge : public thread::decompose_domain {
incremental *instance;
int frame;
const image *delta;
transformation t;
invariant *inv;
image_weighted_avg *accum_image;
protected:
void prepare_subdomains(unsigned int N) {
ale_pos_disable_casting();
ale_real_disable_casting();
ale_accum_disable_casting();
}
void subdomain_algorithm(unsigned int thread,
int i_min, int i_max, int j_min, int j_max) {
point offset = accum_image->offset();
assert (accum_image != NULL);
assert (delta != NULL);
const filter::ssfe *_ssfe = inv->ssfe();
for (int i = i_min; i < i_max; i++)
for (int j = j_min; j < j_max; j++) {
#if 0
/*
* This is untested, but it should work, and is less
* verbose than what follows.
*/
instance->_merge_pixel(frame, delta, t, i, j, _ssfe);
#else
if (_ssfe->ex_is_honored() && instance->is_excluded_r(i, j, frame))
continue;
if (accum_image->accumulate_norender(i, j))
continue;
/*
* Pixel value to be merged, and the associated
* confidence
*/
pixel value, confidence;
if (exposure::get_confidence() != 0) {
_ssfe->filtered(i, j, frame, &value, &confidence,
((pixel) accum_image->get_pixel(i, j)),
accum_image->get_weights()->get_pixel(i, j));
} else {
_ssfe->filtered(i, j, frame, &value, &confidence);
}
accum_image->accumulate(i, j, frame, value, confidence);
#endif
}
}
void finish_subdomains(unsigned int N) {
ale_pos_enable_casting();
ale_real_enable_casting();
ale_accum_enable_casting();
}
public:
merge(incremental *_instance,
int _frame,
const image *_delta,
transformation _t) : decompose_domain(0, _instance->accum_image->height(),
0, _instance->accum_image->width()),
t(_t) {
instance = _instance;
frame = _frame;
delta = _delta;
t = _t;
inv = instance->inv;
accum_image = instance->accum_image;
}
};
void
_merge(int frame, const image *delta, transformation t) {
ui::get()->d2_incremental_start();
point offset = accum_image->offset();
assert (accum_image != NULL);
assert (delta != NULL);
const filter::ssfe *_ssfe = inv->ssfe();
_ssfe->set_parameters(t, delta, offset);
merge m(this, frame, delta, t);
m.run();
ui::get()->d2_incremental_stop();
}
public:
/*
* Constructor
*/
incremental(invariant *inv) {
this->inv = inv;
accum_image = NULL;
}
/*
* Invariant
*/
const invariant *get_invariant() const {
return inv;
}
/*
* Result of rendering.
*/
virtual const image *get_image() const {
assert (accum_image != NULL);
return accum_image;
}
/*
* Definition map. Unit-depth image whose pixels are nonzero where
* the image is defined.
*/
virtual const image *get_defined() const {
assert (accum_image != NULL);
return accum_image->get_weights();
}
/*
* Perform the current rendering step.
*/
virtual void step() {
/*
* Dynamic invariants are not incrementally updated.
*/
if (inv->ssfe()->get_scaled_filter()->is_dynamic()) {
/*
* Create a trivial image for the case where there is
* no chain suffix.
*/
if (accum_image == NULL)
accum_image = new image_weighted_simple(1, 1, 3, inv);
return;
}
assert (get_step() >= -1);
if (get_step() == 0) {
transformation t = align::of(0);
const image *im = image_rw::open(0);
ui::get()->rendering();
if (inv->is_median())
accum_image = new image_weighted_median(1, 1, 3);
else
accum_image = new image_weighted_simple(1, 1, 3, inv);
set_extents_by_map(0, t);
_merge(0, im, t);
image_rw::close(0);
} else if (align::match(get_step())) {
transformation t = align::of(get_step());
ui::get()->rendering();
if (is_extend())
increase_extents_by_map(get_step(), t);
const image *im = image_rw::open(get_step());
_merge(get_step(), im, t);
image_rw::close(get_step());
}
}
virtual void init_point_renderer(unsigned int h, unsigned int w, unsigned int d) {
assert(accum_image == NULL);
if (inv->is_median())
accum_image = new image_weighted_median(h, w, d);
else
accum_image = new image_weighted_simple(h, w, d, inv);
assert(accum_image);
}
virtual void point_render(unsigned int i, unsigned int j, unsigned int f, transformation t) {
const image *im = d2::image_rw::get_open(f);
const filter::ssfe *_ssfe = inv->ssfe();
_ssfe->set_parameters(t, im, accum_image->offset());
_merge_pixel(f, im, t, i, j, _ssfe);
}
virtual void finish_point_rendering() {
return;
}
void free_memory() {
delete accum_image;
accum_image = NULL;
}
};
#endif

117
d2/render/invariant.h Normal file
View File

@@ -0,0 +1,117 @@
// 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
*/
#ifndef __invariant_h__
#define __invariant_h__
#include "../filter.h"
/*
* Class for incremental renderer invariants.
*/
#define min 0
#define max 1
#define avg 2
#define first 3
#define last 4
#define median 5
#define avgf 6
class invariant {
public:
int type;
double type_param;
filter::ssfe *s;
invariant(filter::ssfe *s) {
this->s = s;
type = 2;
type_param = 0;
}
int equals(const invariant *i) const {
return (i->type == type
&& i->type_param == type_param
&& s->equals(i->ssfe()));
}
const filter::ssfe *ssfe() const {
return s;
}
int is_max() const {
return type == max;
}
int is_min() const {
return type == min;
}
int is_avg() const {
return type == avg;
}
int is_avgf() const {
return type == avgf;
}
int is_avgx() const {
return (type == avg || type == avgf);
}
int is_first() const {
return type == first;
}
int is_last() const {
return type == last;
}
int is_median() const {
return type == median;
}
void set_max() {
type = max;
}
void set_min() {
type = min;
}
void set_avg() {
type = avg;
}
void set_avgf(double p) {
type = avgf;
type_param = p;
}
void set_first() {
type = first;
}
void set_last() {
type = last;
}
void set_median() {
type = median;
}
double get_param() {
return type_param;
}
~invariant() {
delete s;
}
};
#undef min
#undef max
#undef avg
#undef first
#undef last
#endif

1388
d2/render/ipc.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,329 @@
// 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
*/
#ifndef __backprojector_h__
#define __backprojector_h__
#include "../../point.h"
#include "rasterizer.h"
#include "raster.h"
/*
* Backprojector for rasterized PSFs.
*
* This class converts a rasterized PSF into a rasterized backprojection array.
*/
class backprojector : public raster {
raster *input;
public:
unsigned int varieties() const {
return input->varieties();
}
unsigned int select(unsigned int i, unsigned int j) const {
return input->select(i, j);
}
private:
/*
* Backprojection for the Irani-Peleg renderer
*
* Applying a special case of theorem 4.1 of the source paper by Irani
* and Peleg, convergence can be assured for a single image, with
* uniform PSF, no change in sampling rate, and taking the normalizing
* divisor c == 1, if H[psf](f)H[aux](f) is real and within the open
* interval (0, 2), where H[psf] is the frequency-domain representation
* of the point-spread function and H[aux] is the frequency-domain
* representation of the backprojection kernel. We can guarantee that
* H[psf](f)H[aux](f) is real by making H[aux](f) == k(f)H[psf](f)*,
* where k is a real function and '*' indicates the complex conjugate.
* If k(f) is equal to 1 for all f, then this is equivalent to the
* condition h[aux](x) == h[psf](-x), where h[] are the time domain
* representations of the respective functions. Since this negation
* of position is implicitly performed in ipc.h, we don't perform it
* here.
*
* However, to ensure that the range (0, 2) is satisfied, it may be
* necessary for k(f) to assume a value other than 1. We choose a
* constant function k, in accordance with the source paper's
* normalizing divisor c, but this is not required. We use FFTW
* when available, but it is likely that common cases will not observe
* any speed improvement.
*/
void initialize_response_array(ale_real *response_array) {
int cols = _filter_dim_j;
int rows = _filter_dim_i;
#ifdef USE_FFTW
fftw_complex *inout;
fftw_plan p_forward;
fftw_plan p_backward;
inout = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * cols * rows);
p_forward = fftw_plan_dft_2d(rows, cols, inout, inout, FFTW_FORWARD, FFTW_ESTIMATE);
p_backward = fftw_plan_dft_2d(rows, cols, inout, inout, FFTW_BACKWARD, FFTW_ESTIMATE);
for (int k = 0; k < 3; k++) {
for (int i = 0; i < rows * cols; i++) {
/*
* Write the values to the FFTW input array,
* shifting by (rows * cols - 1) / 2 in order
* to accommodate the implicit translation.
*/
inout[i][0] = response_array[((i + (rows * cols - 1)/2) * 3 + k)
% (rows * cols * 3)];
inout[i][1] = 0;
}
fftw_execute(p_forward);
/*
* Find the frequency with maximum magnitude, then
* adjust this according to the sampling rate
* (filter resolution).
*/
ale_real max_magnitude = 0;
for (int i = 0; i < rows * cols; i++) {
ale_real input_magnitude;
input_magnitude = sqrt(pow(inout[i][0], 2) + pow(inout[i][1], 2));
if (input_magnitude > max_magnitude)
max_magnitude = input_magnitude;
}
max_magnitude *= (4 * _height * _width) / (rows * cols);
/*
* Scale the magnitude of all of the frequencies and perform
* conjugation.
*/
for (int i = 0; i < rows * cols; i++) {
/*
* Adjust the magnitude
*
* Note: since we're currently dividing all frequencies
* by the same value, there's no need to divide in the
* frequency domain. However, we might want to do
* something else in the future, so it might be
* good to leave the code like this for now.
*/
inout[i][0] = inout[i][0] * pow(0.9 / max_magnitude, 2);
inout[i][1] = inout[i][1] * pow(0.9 / max_magnitude, 2);
/*
* Perform conjugation
*
* Note: conjugation is implicit in ipc.h, so we omit the
* step here.
*/
/* inout[i][1] = -inout[i][1]; */
}
fftw_execute(p_backward);
for (int i = 0; i < rows * cols; i++) {
/*
* Read the values from the FFTW output array,
* shifting by (rows * cols - 1) / 2 in order
* to accommodate the implicit translation.
*/
response_array[((i + (rows * cols - 1)/2) * 3 + k)
% (rows * cols * 3)]
= inout[i][0]
/ (rows * cols);
}
}
fftw_destroy_plan(p_forward);
fftw_destroy_plan(p_backward);
fftw_free(inout);
#else
for (int k = 0; k < 3; k++) {
ale_real *real1 = (ale_real *) calloc(rows * cols, sizeof(ale_real));
ale_real *imag1 = (ale_real *) calloc(rows * cols, sizeof(ale_real));
ale_real *real2 = (ale_real *) calloc(rows * cols, sizeof(ale_real));
ale_real *imag2 = (ale_real *) calloc(rows * cols, sizeof(ale_real));
assert (real1 && imag1 && real2 && imag2);
if (!(real1 && imag1 && real2 && imag2)) {
fprintf(stderr, "Unable to allocate memory in backprojector.\n");
exit(1);
}
/*
* Calculate frequencies. We implement the equations indicated by
* the FFTW3 info page (section "What FFTW Really Computes").
*/
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
for (int jj = 0; jj < cols; jj++) {
real1[i * cols + j] += response_array[((i * cols + jj +
(rows * cols - 1)/2) * 3 + k)
% (rows * cols * 3)]
* (ale_real) cos((-2 * M_PI * j * jj) / cols);
imag1[i * cols + j] += response_array[((i * cols + jj +
(rows * cols - 1)/2) * 3 + k)
% (rows * cols * 3)]
* (ale_real) sin((-2 * M_PI * j * jj) / cols);
}
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
for (int ii = 0; ii < rows; ii++) {
real2[i * cols + j] += real1[ii * cols + j]
* (ale_real) cos((-2 * M_PI * i * ii) / rows)
- imag1[ii * cols + j]
* (ale_real) sin((-2 * M_PI * i * ii) / rows);
imag2[i * cols + j] += real1[ii * cols + j]
* (ale_real) sin((-2 * M_PI * i * ii) / rows)
+ imag1[ii * cols + j]
* (ale_real) cos((-2 * M_PI * i * ii) / rows);
}
/*
* Find the frequency with maximum magnitude, then
* adjust this according to the sampling rate
* (filter resolution).
*/
ale_real max_magnitude = 0;
for (int i = 0; i < rows * cols; i++) {
ale_real input_magnitude;
input_magnitude = sqrt(pow(real2[i], 2) + pow(imag2[i], 2));
if (input_magnitude > max_magnitude)
max_magnitude = input_magnitude;
}
max_magnitude *= (4 * _height * _width) / (rows * cols);
for (int i = 0; i < rows * cols; i++)
response_array[i * 3 + k] *= pow(0.9 / max_magnitude, 2);
free(real1);
free(imag1);
free(real2);
free(imag2);
}
#endif
}
public:
backprojector (raster *input) {
this->input = input;
_height = -input->min_i();
assert (input->max_i() == _height);
_width = -input->min_j();
assert (input->max_j() == _width);
/*
* The element structure matches that of the input.
*/
_filter_dim_i = input->max_elem_i();
_filter_dim_j = input->max_elem_j();
/*
* Ensure that the array has an odd number of elements in each
* direction. This allows us to move the center to the right
* place when using a discrete FT.
*/
assert (_filter_dim_i % 2 == 1);
assert (_filter_dim_j % 2 == 1);
/*
* Determine the number of arrays to create.
*/
num_arrays = input->varieties();
/*
* Create arrays
*/
response_arrays = (ale_real **)malloc(num_arrays * sizeof(ale_real *));
if (!response_arrays) {
fprintf(stderr, "Could not allocate in backprojector.\n");
exit(1);
}
for (unsigned int n = 0; n < num_arrays; n++) {
response_arrays[n] = (ale_real *)malloc(_filter_dim_i * _filter_dim_j * 3
* sizeof(ale_real));
if (!response_arrays[n]) {
fprintf(stderr, "Could not allocate in backprojector.\n");
exit(1);
}
for (unsigned int i = 0; i < _filter_dim_i; i++)
for (unsigned int j = 0; j < _filter_dim_j; j++)
for (unsigned int k = 0; k < 3; k++) {
response_arrays[n][i * _filter_dim_j * 3 + j * 3 + k]
= input->element(n, i, j, k);
}
initialize_response_array(response_arrays[n]);
}
#if 0
avg_response = (ale_real *)malloc(_filter_dim_i * _filter_dim_j * 3
* sizeof(ale_real));
if (!avg_response) {
fprintf(stderr, "Could not allocate in backprojector.\n");
exit(1);
}
for (unsigned int i = 0; i < _filter_dim_i; i++)
for (unsigned int j = 0; j < _filter_dim_j; j++)
for (unsigned int k = 0; k < 3; k++) {
avg_response[i * _filter_dim_j * 3 + j * 3 + k]
= input->element(i, j, k);
}
initialize_response_array(avg_response);
#endif
compute_integrals();
}
};
#endif

82
d2/render/psf/box.h Normal file
View File

@@ -0,0 +1,82 @@
// 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
*/
#ifndef __psf_box_h__
#define __psf_box_h__
#include "../../point.h"
#include "psf.h"
/*
* Point-spread function module.
*
* This module implements the box filter.
*/
class box : public psf {
ale_real _radius;
public:
/*
* The following four functions indicate filter boundaries. Filter
* support may include everything up to and including the boundaries
* specified here.
*/
ale_real min_i() const { return -_radius; }
ale_real max_i() const { return _radius; }
ale_real min_j() const { return -_radius; }
ale_real max_j() const { return _radius; }
/*
* Response function
*
* Get the response to the rectangle bounded by (top, bot, lef, rig).
* This function must correctly handle points which fall outside of the
* filter support. The variety the responding pixel is provided, in
* case response is not uniform for all pixels (e.g. some sensor arrays
* stagger red, green, and blue sensors).
*/
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig,
unsigned int variety) const {
psf_result result;
if (top < min_i())
top = min_i();
if (bot > max_i())
bot = max_i();
if (lef < min_j())
lef = min_j();
if (rig > max_j())
rig = max_j();
if (bot > top && rig > lef)
for (int k = 0; k < 3; k++)
result.matrix(k, k) = (bot - top) * (rig - lef) / (4 * _radius * _radius);
return result;
}
box(ale_real radius) {
_radius = radius;
}
};
#endif

85
d2/render/psf/circle.h Normal file
View File

@@ -0,0 +1,85 @@
// Copyright 2003, 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 __psf_circle_h__
#define __psf_circle_h__
#include "../../point.h"
#include "psf.h"
/*
* Point-spread function module.
*
* This module implements a circular filter.
*/
class circle : public psf {
ale_real _radius;
public:
/*
* The following four functions indicate filter boundaries. Filter
* support may include everything up to and including the boundaries
* specified here.
*/
ale_real min_i() const { return -_radius; }
ale_real max_i() const { return _radius; }
ale_real min_j() const { return -_radius; }
ale_real max_j() const { return _radius; }
/*
* Response function
*
* Get the response to the rectangle bounded by (top, bot, lef, rig).
* This function must correctly handle points which fall outside of the
* filter support. The variety of the responding pixel is provided, in
* case response is not uniform for all pixels (e.g. some sensor arrays
* stagger red, green, and blue sensors).
*/
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig,
unsigned int variety) const {
psf_result result;
for (int k = 0; k < 3; k++)
result.matrix(k, k) = 0;
ale_real total = (bot - top) * (rig - lef) / (M_PI * _radius * _radius);
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++) {
ale_real one_half = 1 / (ale_real) 2;
ale_real r = pow(top + (bot - top) * ((i + one_half) / (ale_real) 10), 2)
+ pow(lef + (rig - lef) * ((j + one_half) / (ale_real) 10), 2);
if (r < _radius * _radius)
for (int k = 0; k < 3; k++)
result.matrix(k, k) += (total / (ale_real) 100);
}
return result;
}
circle(ale_real radius) {
_radius = radius;
}
};
#endif

145
d2/render/psf/convolution.h Normal file
View File

@@ -0,0 +1,145 @@
// Copyright 2003, 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 __psf_convolution_h__
#define __psf_convolution_h__
#include "../../point.h"
#include "psf.h"
/*
* XXX: This doesn't work yet.
*/
/*
* Point-spread function module.
*
* This module implements the convolution (f1 * f2) of point-spread functions f1 and
* f2.
*/
class convolution : public psf {
ale_pos _radius;
psf *f1, *f2;
ale_real _min_i, _max_i, _min_j, _max_j;
public:
/*
* The following four functions indicate filter boundaries. Filter
* support may include everything up to and including the boundaries
* specified here.
*/
ale_real min_i() const { return _min_i; }
ale_real max_i() const { return _max_i; }
ale_real min_j() const { return _min_j; }
ale_real max_j() const { return _max_j; }
/*
* Get the number of varieties supported by this PSF. These usually
* correspond to different points in the sensor array.
*/
virtual unsigned int varieties() {
return f1->varieties() * f2->varieties();
}
/*
* Select the variety appropriate for a given position in the sensor
* array.
*/
virtual unsigned int select(unsigned int i, unsigned int j) {
return (f1->select(i, j) * f2->varieties() + f2->select(i, j));
}
/*
* Response function
*
* Get the response to the rectangle bounded by (top, bot, lef, rig).
* This function must correctly handle points which fall outside of the
* filter support. The variety of the responding pixel is provided, in
* case response is not uniform for all pixels (e.g. some sensor arrays
* stagger red, green, and blue sensors).
*/
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig,
unsigned int variety) const {
psf_result result;
psf_result r1, r2;
unsigned int v1 = variety / f2->varieties();
unsigned int v2 = variety % f2->varieties();
/*
* This code uses a rasterized approximation of the filters involved.
*/
ale_real vertical_center = (top + bot) / 2;
ale_real horizontal_center = (lef + rig) / 2;
ale_real vertical_resolution = bot - top;
ale_real horizontal_resolution = rig - lef;
if (!(vertical_resolution > 0
&& horizontal_resolution > 0))
return result; /* zero */
for (ale_real i = f1->min_i() + (vertical_resolution / 2);
i < f1->max_i() - (vertical_resolution / 2);
i += vertical_resolution)
for (ale_real j = f1->min_j() + (horizontal_resolution / 2);
j < f1->max_j() - (horizontal_resolution / 2);
j += horizontal_resolution) {
ale_real t = i - (vertical_resolution / 2);
ale_real b = i + (vertical_resolution / 2);
ale_real l = j - (horizontal_resolution / 2);
ale_real r = j + (horizontal_resolution / 2);
ale_real vc = vertical_center;
ale_real hc = horizontal_center;
r1 = (*f1)(t, b, l, r, v1);
r2 = (*f2)(vc - b, vc - t, hc - r, hc - l, v2);
for (int k1 = 0; k1 < 3; k1++)
for (int k2 = 0; k2 < 3; k2++)
result.set_matrix(k1, k2, result.get_matrix(k1, k2)
+ r1.get_matrix(k1, k2)
* r2.get_matrix(k1, k2));
}
return result;
}
convolution(psf *f1, psf *f2) {
this->f1 = f1;
this->f2 = f2;
/*
* XXX: I'm fairly sure that this is correct for filters with
* zero-centered bounding boxes, and I _think_ it's correct for
* other filters also, but I haven't formally proven this.
*/
_min_i = f1->min_i() + f2->min_i();
_min_j = f1->min_j() + f2->min_j();
_max_i = f1->max_i() + f2->max_i();
_max_j = f1->max_j() + f2->max_j();
}
};
#endif

127
d2/render/psf/gauss.h Normal file
View File

@@ -0,0 +1,127 @@
// code by HJ Hornbeck, based on code copyright 2003, 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 __psf_gauss_h__
#define __psf_gauss_h__
#include "../../point.h"
#include "psf.h"
/*
* A Gaussian point-spread function. It's aimed at duplicating the most common type
* of blurring in many optical systems. It is uniform across the entire image, so
* it can't correct for poor focus at the edges.
*/
#define D2_GAUSS_CUTOFF ((ale_real) 2.0)
class gauss : public psf {
ale_real sigma; // radius, in pixels per standard deviation
/*
* Disabled the following definition because some compilers may not be
* able to handle static const definitions within a class (and because
* the C++ specification may disallow such for non-integral types,
* anyway).
*
* -- dhilvert@auricle.dyndns.org 18-May-2007
*/
// static const ale_pos cutoff = 2; // standard deviations before we cut off
// helper variables
ale_real radius;
ale_real sigma_premult;
public:
/*
* The following four functions indicate filter boundaries. Filter
* support may include everything up to and including the boundaries
* specified here.
*/
inline ale_real max_i() const { return radius; }
inline ale_real min_i() const { return -max_i(); } // we're symmetrical, so it works!
inline ale_real min_j() const { return -max_i(); }
inline ale_real max_j() const { return max_i(); }
/*
* Response function
*
* Get the response to the rectangle bounded by (top, bot, lef, rig).
* This function must correctly handle points which fall outside of the
* filter support. The variety of the responding pixel is provided, in
* case response is not uniform for all pixels (e.g. some sensor arrays
* stagger red, green, and blue sensors).
*/
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig,
unsigned int variety) const {
psf_result result;
// calculate some needed values
ale_pos area_premult = (bot - top) * (rig - lef) / 25;
ale_real vert_step = (bot - top) / 4;
ale_real horiz_step = (rig - lef) / 4;
ale_real total = 0;
// determine the final value by simple sampling:
for (ale_real i = top; i < bot + vert_step / 2; i += vert_step)
for (ale_real j = lef; j < rig + horiz_step / 2; j += horiz_step) {
// calculate radius for given sample
ale_real r = sqrt( i*i + j*j );
if ( r < radius ) // calculate gaussian falloff
total += exp( -r * r * sigma_premult ) ;
// outside our radius? must be 0...
}
// adjust for point sampling and area
total *= area_premult;
// pre-fill the colour result matrix
for (int k = 0; k < 3; k++)
result.matrix(k, k) = 0;
// fill in the results
for (int k = 0; k < 3; k++)
result.matrix(k, k) = total;
return result;
}
/*
* Our glorious constructor
*/
gauss(ale_real sig) {
sigma = sig;
// fill in our helper variables
radius = sigma * D2_GAUSS_CUTOFF;
sigma_premult = 1 / (sigma * sigma);
}
};
#undef D2_GAUSS_CUTOFF
#endif

141
d2/render/psf/normalizer.h Normal file
View File

@@ -0,0 +1,141 @@
// 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
*/
#ifndef __normalizer_h__
#define __normalizer_h__
#include "../../point.h"
#include "rasterizer.h"
#include "raster.h"
/*
* Normalizer for rasterized PSFs.
*
* This class normalizes a rasterized PSF.
*/
class normalizer : public raster {
raster *input;
public:
unsigned int varieties() const {
return input->varieties();
}
unsigned int select(unsigned int i, unsigned int j) const {
return input->select(i, j);
}
private:
void initialize_response_array(ale_real *response_array) {
pixel integral;
integral = integrate(response_array);
for (unsigned int i = 0; i < _filter_dim_i; i++)
for (unsigned int j = 0; j < _filter_dim_j; j++)
for (unsigned int k = 0; k < 3 ; k++)
response_array[i * _filter_dim_j * 3 + j * 3 + k] /= integral[k];
}
public:
normalizer (raster *input) {
this->input = input;
_height = -input->min_i();
assert (input->max_i() == _height);
_width = -input->min_j();
assert (input->max_j() == _width);
/*
* The element structure matches that of the input.
*/
_filter_dim_i = input->max_elem_i();
_filter_dim_j = input->max_elem_j();
/*
* Ensure that the array has an odd number of elements in each
* direction. This allows us to move the center to the right
* place when using FFTW.
*/
assert (_filter_dim_i % 2 == 1);
assert (_filter_dim_j % 2 == 1);
/*
* Determine the number of arrays to create.
*/
num_arrays = input->varieties();
/*
* Create arrays
*/
response_arrays = (ale_real **)malloc(num_arrays * sizeof(ale_real *));
if (!response_arrays) {
fprintf(stderr, "Could not allocate in normalizer.\n");
exit(1);
}
for (unsigned int n = 0; n < num_arrays; n++) {
response_arrays[n] = (ale_real *)malloc(_filter_dim_i * _filter_dim_j * 3
* sizeof(ale_real));
if (!response_arrays[n]) {
fprintf(stderr, "Could not allocate in normalizer.\n");
exit(1);
}
for (unsigned int i = 0; i < _filter_dim_i; i++)
for (unsigned int j = 0; j < _filter_dim_j; j++)
for (unsigned int k = 0; k < 3; k++) {
response_arrays[n][i * _filter_dim_j * 3 + j * 3 + k]
= input->element(n, i, j, k);
}
initialize_response_array(response_arrays[n]);
}
#if 0
avg_response = (ale_real *)malloc(_filter_dim_i * _filter_dim_j * 3
* sizeof(ale_real));
if (!avg_response) {
fprintf(stderr, "Could not allocate in normalizer.\n");
exit(1);
}
for (unsigned int i = 0; i < _filter_dim_i; i++)
for (unsigned int j = 0; j < _filter_dim_j; j++)
for (unsigned int k = 0; k < 3; k++) {
avg_response[i * _filter_dim_j * 3 + j * 3 + k]
= input->element(i, j, k);
}
initialize_response_array(avg_response);
#endif
compute_integrals();
}
};
#endif

184
d2/render/psf/psf.h Normal file
View File

@@ -0,0 +1,184 @@
// 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
*/
#ifndef __psf_h__
#define __psf_h__
#include "../../point.h"
/*
* Point-spread function module abstract base class.
*/
class psf {
public:
/*
* Result type is a matrix.
*/
class psf_result {
friend class psf;
protected:
ale_real _matrix[3][3];
public:
psf_result() {
/*
* Simplified version -- diagonal matrix
*/
for (int i = 0; i < 3; i++)
_matrix[i][i] = 0;
}
ale_real get_matrix(unsigned int i, unsigned int j) {
assert (i < 3);
assert (j < 3);
/*
* Simplified version -- diagonal matrix
*/
if (i != j)
return 0;
else
return _matrix[i][j];
}
void set_matrix(unsigned int i, unsigned int j, ale_real value) {
assert (i < 3);
assert (j < 3);
/*
* Simplified version -- diagonal matrix
*/
assert (i == j || value == 0);
_matrix[i][j] = value;
}
ale_real &matrix(unsigned int i, unsigned int j) {
assert (i < 3);
assert (j < 3);
/*
* Simplified version -- diagonal matrix
*/
assert (i == j);
return _matrix[i][j];
}
pixel operator()(pixel p) {
/*
* Simplified version -- diagonal matrix
*/
return pixel(_matrix[0][0] * p[0],
_matrix[1][1] * p[1],
_matrix[2][2] * p[2]);
}
/*
* Weights associated with the result
*/
pixel weight() {
/*
* Simplified version -- diagonal matrix
*/
return pixel(
_matrix[0][0],
_matrix[1][1],
_matrix[2][2]);
}
void operator*=(ale_real scale) {
/*
* Simplified version -- diagonal matrix
*/
for (int i = 0; i < 3; i++)
_matrix[i][i] *= scale;
}
};
/*
* The following four functions indicate filter boundaries. Filter
* support may include everything up to and including the boundaries
* specified here.
*/
virtual ale_real min_i() const = 0;
virtual ale_real max_i() const = 0;
virtual ale_real min_j() const = 0;
virtual ale_real max_j() const = 0;
/*
* Get the number of varieties supported by this PSF. These usually
* correspond to different points in the sensor array.
*/
virtual unsigned int varieties() const {
return 1;
}
/*
* Select the variety appropriate for a given position in the sensor
* array.
*/
virtual unsigned int select(unsigned int i, unsigned int j) {
return 0;
}
/*
* Get the response to the rectangle bounded by (top, bot, lef, rig).
* This function must correctly handle points which fall outside of the
* filter support. One of several varieties can be selected, usually
* based on position (e.g. some sensor arrays stagger red, green, and
* blue sensors).
*/
virtual psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real
rig, unsigned int variety) const = 0;
virtual psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real
rig, unsigned int variety, char channels) const {
return operator()(top, bot, lef, rig, variety);
}
#if 0
/*
* Get the average pixel response. This function should be overloaded
* for PSFs that support multiple varieties.
*/
virtual psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig) const {
return operator()(top, bot, lef, rig, 0);
}
#endif
virtual ~psf() {
}
};
#endif

View File

@@ -0,0 +1,333 @@
// 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
*/
#ifndef __psf_calibrate_h__
#define __psf_calibrate_h__
#include "../../image.h"
#include "../../render.h"
#include "../ipc.h"
class psf_calibrate : public ipc {
private:
double *psf_match_args;
public:
psf_calibrate(render *input, unsigned int iterations, int _inc, psf *lresponse, psf *nlresponse,
double *psf_match_args)
: ipc(input, iterations, _inc, lresponse, nlresponse, 1, 0, 0) {
fprintf(stderr, "\nIPC Calibration module.\n\n");
fprintf(stderr, "This module is designed for use with a calibration script.\n\n");
this->psf_match_args = psf_match_args;
}
void _ip_frame(ale_accum *diff, unsigned int *count, int m) {
/*
* Get alignment information for frame m.
*/
transformation t = align::of(m);
/*
* We create real and simulated input-frame data structures
* REAL and SIMULATED, as well as simulated input-frame weights
* SIM_WEIGHTS, used to track the weights of contributions to each
* simulated input-frame pixel component.
*/
const image *real = image_rw::open(m);
image *simulated = new_image_ale_real(
real->height(),
real->width(), 3);
/*
* Calculate the simulated input frame SIMULATED from the image
* approximation APPROXIMATION, iterating over image
* approximation pixels and tracking contributions to simulated
* frame pixels in the data structure SIM_WEIGHTS.
*/
image *sim_weights = new_image_ale_real(
simulated->height(),
simulated->width(), 3);
for (unsigned int i = 0; i < approximation->height(); i++)
for (unsigned int j = 0; j < approximation->width(); j++) {
/*
* Obtain the position Q and dimensions D of
* image approximation pixel (i, j) in the coordinate
* system of the simulated frame.
*/
point p = point(i + approximation->offset()[0], j + approximation->offset()[1]);
point q;
ale_pos d[2];
t.unscaled_map_area_inverse(p, &q, d);
/*
* Iterate over all simulated frame pixels influenced
* by the scene pixel (i, j), as determined by the
* response function.
*/
for (int ii = (int) floor(q[0] - d[0] + (ale_pos) lresponse->min_i());
ii <= ceil(q[0] + d[0] + (ale_pos) lresponse->max_i()); ii++)
for (int jj = (int) floor(q[1] - d[1] + (ale_pos) lresponse->min_j());
jj <= ceil(q[1] + d[1] + (ale_pos) lresponse->max_j()); jj++) {
ale_pos top = q[0] - d[0];
ale_pos bot = q[0] + d[0];
ale_pos lef = q[1] - d[1];
ale_pos rig = q[1] + d[1];
if (ii >= (int) 0
&& ii < (int) real->height()
&& jj >= (int) 0
&& jj < (int) real->width()) {
psf::psf_result r =
(*lresponse)(top - ii, bot - ii,
lef - jj, rig - jj,
lresponse->select(ii, jj));
sim_weights->set_pixel(ii, jj,
(pixel) sim_weights->get_pixel(ii, jj) + r.weight());
simulated->set_pixel(ii, jj,
(pixel) simulated->get_pixel(ii, jj) + r(approximation->get_pixel(i, j)));
}
}
}
/*
* Normalize SIMULATED by SIM_WEIGHTS
*/
for (unsigned int i = 0; i < simulated->height(); i++)
for (unsigned int j = 0; j < simulated->width(); j++)
simulated->set_pixel(i, j,
(pixel) simulated->get_pixel(i, j) / (pixel) sim_weights->get_pixel(i, j));
delete sim_weights;
/*
* If NLRESPONSE is defined, then redefine SIMULATED to account
* for this.
*/
if (nlresponse != NULL) {
image *nlsimulated = new_image_ale_real(
simulated->height(),
simulated->width(), 3);
image *nlsim_weights = new_image_ale_real(
simulated->height(),
simulated->width(), 3);
for (unsigned int i = 0; i < simulated->height(); i++)
for (unsigned int j = 0; j < simulated->width(); j++) {
for (int ii = (int) floor(i - 0.5 + nlresponse->min_i());
ii <= ceil(i + 0.5 + nlresponse->max_i()); ii++)
for (int jj = (int) floor(j - 0.5 + nlresponse->min_j());
jj <= ceil(j + 0.5 + nlresponse->max_j()); jj++) {
ale_pos top = i - 0.5;
ale_pos bot = i + 0.5;
ale_pos lef = j - 0.5;
ale_pos rig = j + 0.5;
if (ii >= (int) 0
&& ii < (int) nlsimulated->height()
&& jj >= (int) 0
&& jj < (int) nlsimulated->width()) {
psf::psf_result r =
(*nlresponse)(top - ii, bot - ii,
lef - jj, rig - jj,
nlresponse->select(ii, jj));
nlsim_weights->set_pixel(ii, jj,
(pixel) nlsim_weights->get_pixel(ii, jj) + r.weight());
nlsimulated->set_pixel(ii, jj,
(pixel) nlsimulated->get_pixel(ii, jj) + r(real->exp().unlinearize(simulated->get_pixel(i, j))));
}
}
}
/*
* Normalize nlsimulated.
*/
for (unsigned int i = 0; i < simulated->height(); i++)
for (unsigned int j = 0; j < simulated->width(); j++)
nlsimulated->set_pixel(i, j,
(pixel) nlsimulated->get_pixel(i, j) / nlsim_weights->get_pixel(i, j));
/*
* Linearize nlsimulated
*/
for (unsigned int i = 0; i < simulated->height(); i++)
for (unsigned int j = 0; j < simulated->width(); j++)
nlsimulated->set_pixel(i, j,
real->exp().linearize(nlsimulated->get_pixel(i, j)));
delete simulated;
delete nlsim_weights;
simulated = nlsimulated;
}
/*
* For each SIMULATED pixel, calculate the difference from
* the corresponding REAL pixel, and update the sum of squares
* of differences.
*/
ale_real margin_i1 = lresponse->min_i() + (nlresponse ? nlresponse->min_i() : ale_real_0);
ale_real margin_i2 = lresponse->max_i() + (nlresponse ? nlresponse->max_i() : ale_real_0);
ale_real margin_j1 = lresponse->min_j() + (nlresponse ? nlresponse->min_j() : ale_real_0);
ale_real margin_j2 = lresponse->max_j() + (nlresponse ? nlresponse->max_j() : ale_real_0);
for (unsigned int i = 0; i < simulated->height(); i++)
for (unsigned int j = 0; j < simulated->width(); j++) {
/*
* Establish margins. This is designed to reduce the
* influence of boundary conditions.
*/
point p;
p = t.transform_unscaled(point(i + margin_i1, j + margin_j1));
if (p[0] < 0 || p[0] > approximation->height()
|| p[1] < 0 || p[1] > approximation->width())
continue;
p = t.transform_unscaled(point(i + margin_i1, j + margin_j2));
if (p[0] < 0 || p[0] > approximation->height()
|| p[1] < 0 || p[1] > approximation->width())
continue;
p = t.transform_unscaled(point(i + margin_i2, j + margin_j1));
if (p[0] < 0 || p[0] > approximation->height()
|| p[1] < 0 || p[1] > approximation->width())
continue;
p = t.transform_unscaled(point(i + margin_i2, j + margin_j2));
if (p[0] < 0 || p[0] > approximation->height()
|| p[1] < 0 || p[1] > approximation->width())
continue;
/*
* Real and simulated responses
*/
pixel comp_real = real->get_pixel(i, j);
pixel comp_simu = simulated->get_pixel(i, j);
for (unsigned int k = 0; k < simulated->depth(); k++) {
if (!finite(comp_simu[k]))
continue;
/*
* Error calculation
*/
if ((comp_real[k] < 1.0 || comp_simu[k] < 1.0 )
&& (comp_real[k] > 0 || comp_simu[k] > 0)
&& ((*count) < ULONG_MAX)) {
/*
* real and simulated are distinguishable
* within the dynamic range of the program
* inputs, so calculate the error for this
* channel.
*/
(*diff) += pow(comp_simu[k] - comp_real[k], 2);
(*count)++;
}
}
}
image_rw::close(m);
delete simulated;
}
void _ip() {
/*
* Input images 0 through count()-2 are frames captured with
* the device to be calibrated, so we combine the difference
* values for all of these frames against the calibration image
* count()-1.
*/
ale_accum diff = 0;
unsigned int channel_count = 0;
approximation = image_rw::copy(image_rw::count() - 1, "PSF_CALIBRATE reference");
#if 0
fprintf(stderr, "[%f %f %f %f %f %f] ", psf_match_args[0],
psf_match_args[1],
psf_match_args[2],
psf_match_args[3],
psf_match_args[4],
psf_match_args[5]);
#endif
for (unsigned int i = 0; i < approximation->height(); i++)
for (unsigned int j = 0; j < approximation->width(); j++) {
approximation->set_pixel(i, j, (pixel) approximation->get_pixel(i, j)
* pixel(psf_match_args[0],
psf_match_args[1],
psf_match_args[2]));
approximation->set_pixel(i, j, (pixel) approximation->get_pixel(i, j)
+ pixel(psf_match_args[3],
psf_match_args[4],
psf_match_args[5]));
}
for (unsigned int m = 0; m < image_rw::count() - 1; m++) {
_ip_frame(&diff, &channel_count, m);
}
diff = pow(diff / (ale_accum) channel_count, 0.5);
fprintf(stderr, "\n\nPSF Error:: %e\n\n", (double) diff);
delete approximation;
}
void free_memory() {
}
};
#endif

197
d2/render/psf/psf_parse.h Normal file
View File

@@ -0,0 +1,197 @@
// 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
*/
#ifndef __psf_parse_h__
#define __psf_parse_h__
#include "psf.h"
#include "box.h"
#include "circle.h"
#include "gauss.h"
#include "sum.h"
#include "convolution.h"
#include "scalar_mult.h"
#include "stdin.h"
#include "stdin_vg.h"
/*
* Parse strings describing point-spread functions, and return a psf object
* satisfying the string.
*/
class psf_parse {
private:
static int strpfix(const char *a, const char *b) {
return strncmp(a, b, strlen(a));
}
static void nomem() {
fprintf(stderr, "\n\n*** Error: unable to allocate memory in psf_parse. ***\n\n");
exit(1);
}
static void syntax_error(const char *explanation) {
fprintf(stderr, "\n\n*** Error: PSF syntax: %s ***\n\n", explanation);
exit(1);
}
/*
* Evaluate a type string having no remaining binary operators.
*/
static psf *get_atomic(int is_linear, const char *orig_type) {
double param;
if (!strcmp(orig_type, "stdin")) {
fprintf(stderr, "\nInitializing ");
fprintf(stderr, is_linear ? "linear" : "non-linear");
fprintf(stderr, " PSF.\n");
return new psf_stdin();
} else if (!strcmp(orig_type, "stdin_vg")) {
fprintf(stderr, "\nInitializing ");
fprintf(stderr, is_linear ? "linear" : "non-linear");
fprintf(stderr, " PSF.\n");
return new psf_stdin_vg();
} else if (!strpfix("box=", orig_type)) {
if (sscanf(orig_type + strlen("box="), "%lf", &param) != 1)
syntax_error("Unable to get box diameter.");
return new box(param / 2);
} else if (!strpfix("circle=", orig_type)) {
if (sscanf(orig_type + strlen("circle="), "%lf", &param) != 1)
syntax_error("Unable to get circle diameter.");
return new circle(param / 2);
} else if (!strpfix("gauss=", orig_type)) {
if (sscanf(orig_type + strlen("gauss="), "%lf", &param) != 1)
syntax_error("Unable to parse gauss std deviation.");
return new gauss(param / 2);
} else {
fprintf(stderr, "get_atomic type %s\n", orig_type);
syntax_error("Unable to get filter.");
}
assert(0);
}
/*
* Get a scalar value
*/
static ale_real get_scalar(const char *orig_type) {
double result;
if (sscanf(orig_type, "%lf", &result) != 1)
syntax_error("Unable to get scalar value.");
return result;
}
/*
* Split a type string with the binary operator having
* third-lowest precedence (i.e., scalar multiplication).
*/
static psf *get_scalar_mult(int is_linear, const char *orig_type) {
char *type = strdup(orig_type);
char *operator_index = (char *) type;
assert(type);
if (!type)
nomem();
while (*operator_index != '\0'
&& *operator_index != '*')
operator_index++;
if (*operator_index == '\0') {
free(type);
return get_atomic(is_linear, orig_type);
}
*operator_index = '\0';
psf *result = new scalar_mult(get_scalar(type), get_scalar_mult(is_linear, operator_index + 1));
*operator_index = '*';
free(type);
return result;
}
/*
* Split a type string with the binary operator having
* second-lowest precedence (i.e., convolution).
*/
static psf *get_convolution(int is_linear, const char *orig_type) {
char *type = strdup(orig_type);
char *operator_index = (char *) type;
assert(type);
if (!type)
nomem();
while (*operator_index != '\0'
&& *operator_index != '^')
operator_index++;
if (*operator_index == '\0') {
free(type);
return get_scalar_mult(is_linear, orig_type);
}
*operator_index = '\0';
psf *result = new convolution(get_scalar_mult(is_linear, type), get_convolution(is_linear, operator_index + 1));
*operator_index = '^';
free(type);
return result;
}
/*
* Split the type string using the binary operator with
* lowest precedence (addition).
*/
static psf *get_summation(int is_linear, const char *orig_type) {
char *type = strdup(orig_type);
char *plus_index = (char *) type;
assert(type);
if (!type)
nomem();
while (*plus_index != '\0'
&& *plus_index != '+')
plus_index++;
if (*plus_index == '\0') {
free(type);
return get_convolution(is_linear, orig_type);
}
*plus_index = '\0';
psf *result = new sum(get_convolution(is_linear, type), get_summation(is_linear, plus_index + 1));
*plus_index = '+';
free(type);
return result;
}
public:
static psf *get(int is_linear, const char *orig_type) {
return get_summation(is_linear, orig_type);
}
};
#endif

View File

@@ -0,0 +1,109 @@
// 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
*/
#ifndef __psf_template_h__
#define __psf_template_h__
#include "psf.h"
/*
* Point-spread function template.
*/
template <unsigned int rows, unsigned int cols>
class psf_template : public psf {
const ale_real (&response)[rows][cols][3];
ale_pos height, width;
public:
psf_template(ale_pos h, ale_pos w, const ale_real (&_response)[rows][cols][3]) : response(_response) {
height = h / 2;
width = w / 2;
}
/*
* The following four functions indicate filter boundaries. Filter
* support may include everything up to and including the boundaries
* specified here.
*/
ale_real min_i() const { return -height; }
ale_real max_i() const { return height; }
ale_real min_j() const { return -width; }
ale_real max_j() const { return width; }
/*
* Response function
*
* Get the response to the rectangle bounded by (top, bot, lef, rig).
* This function must correctly handle points which fall outside of the
* filter support. The variety of the responding pixel is
* provided, in case response is not uniform for all pixels
* (e.g. some sensor arrays stagger red, green, and blue
* sensors).
*/
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig,
unsigned int variety) const {
psf_result result;
if (top < min_i())
top = min_i();
if (bot > max_i())
bot = max_i();
if (lef < min_j())
lef = min_j();
if (rig > max_j())
rig = max_j();
int il = (int) floor((top - min_i()) / (max_i() - min_i()) * rows);
int ih = (int) floor((bot - min_i()) / (max_i() - min_i()) * rows);
int jl = (int) floor((lef - min_j()) / (max_j() - min_j()) * cols);
int jh = (int) floor((rig - min_j()) / (max_j() - min_j()) * cols);
for (int ii = il; ii <= ih; ii++)
for (int jj = jl; jj <= jh; jj++) {
ale_real ltop = ((ale_real) ii) / rows * (max_i() - min_i()) + min_i();
ale_real lbot = ((ale_real) ii + 1) / rows * (max_i() - min_i()) + min_i();
ale_real llef = ((ale_real) jj) / cols * (max_j() - min_j()) + min_j();
ale_real lrig = ((ale_real) jj + 1) / cols * (max_j() - min_j()) + min_j();
if (ltop < top)
ltop = top;
if (lbot > bot)
lbot = bot;
if (llef < lef)
llef = lef;
if (lrig > rig)
lrig = rig;
assert (ii >= 0);
assert (ii < (int) rows);
assert (jj >= 0);
assert (jj < (int) cols);
for (int k = 0; k < 3; k++)
result.matrix(k, k) += ((lbot - ltop) * (lrig - llef)
* response[ii][jj][k]);
}
return result;
}
};
#endif

309
d2/render/psf/raster.h Normal file
View File

@@ -0,0 +1,309 @@
// 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
*/
#ifndef __raster_h__
#define __raster_h__
#include "../../point.h"
#include "psf.h"
/*
* Raster point-spread function.
*/
class raster : public psf {
protected:
ale_real _height;
ale_real _width;
unsigned int _filter_dim_i;
unsigned int _filter_dim_j;
unsigned int num_arrays;
ale_real **response_arrays;
ale_real **response_partials;
#if 0
ale_real *avg_response;
#endif
pixel *response_integrals;
#if 0
pixel avg_integral;
#endif
public:
/*
* The following four functions indicate filter boundaries. Filter
* support may include everything up to and including the boundaries
* specified here.
*/
ale_real min_i() const { return ale_real_unexceptional_negation(_height); }
ale_real max_i() const { return _height; }
ale_real min_j() const { return ale_real_unexceptional_negation(_width); }
ale_real max_j() const { return _width; }
/*
* Element accessor methods.
*/
unsigned int max_elem_i() {
return _filter_dim_i;
}
unsigned int max_elem_j() {
return _filter_dim_j;
}
ale_real element(unsigned int n, unsigned int i, unsigned int j, unsigned int k) {
assert (n < num_arrays);
assert (i < _filter_dim_i);
assert (j < _filter_dim_j);
assert (k < 3);
return response_arrays[n][i * _filter_dim_j * 3 + j * 3 + k];
}
#if 0
ale_real element(unsigned int i, unsigned int j, unsigned int k) {
assert (i < _filter_dim_i);
assert (j < _filter_dim_j);
assert (k < 3);
return avg_response[i * _filter_dim_j * 3 + j * 3 + k];
}
#endif
/*
* Response function
*
* Get the response to the rectangle bounded by (top, bot, lef, rig).
* This function must correctly handle points which fall outside of the
* filter support.
*
* generic_response (private):
*
* A response array for this generic function is provided by the
* calling function, and a specific or average response is returned,
* based on this array.
*
* operator():
*
* The index of the specific response array is provided, from which the
* specific response is determined. Alternatively, if no index is
* specified, then the average response is returned.
*/
private:
psf_result generic_response(ale_real *response_partial, ale_real top, ale_real
bot, ale_real lef, ale_real rig, char channels) const {
assert (response_partial != NULL);
psf_result result;
/*
* lrintf() can be more efficient than floor() or float-to-int
* casts. For more details, see Erik de Castro Lopo, "Faster
* Floating Point to Integer Conversions":
*
* http://mega-nerd.com/FPcast/
*
* In this case, lrintf() seems to be a bit faster than plain
* casting, and much faster than floor(0.5 + ...). Casting
* from round() seems to be an acceptable alternative to
* lrintf().
*
* Early calculation of common floating-point constants in the
* following code is based on an initial implementation by HJ
* Hornbeck.
*/
ale_real i_element_scale = (ale_real) _filter_dim_i / (max_i() - min_i());
ale_real j_element_scale = (ale_real) _filter_dim_j / (max_j() - min_j());
int il = (int) lrintf(i_element_scale * (top - min_i()));
int ih = (int) lrintf(i_element_scale * (bot - min_i()));
int jl = (int) lrintf(j_element_scale * (lef - min_j()));
int jh = (int) lrintf(j_element_scale * (rig - min_j()));
/*
* Bounds clamping may be faster when performed in integer
* arithmetic than in floating-point, so we do this after
* float-to-int conversion is complete.
*/
if (il < 0)
il = 0;
if (jl < 0)
jl = 0;
if (ih > (int) _filter_dim_i)
ih = (int) _filter_dim_i;
if (jh > (int) _filter_dim_j)
jh = (int) _filter_dim_j;
if (!(il < ih) || !(jl < jh))
return result;
for (int k = 0; k < 3; k++) {
if (!((1 << k) & channels))
continue;
assert (ih > 0 && jh > 0);
assert (ih <= (int) _filter_dim_i);
assert (jh <= (int) _filter_dim_j);
ale_real result_k = 0;
if (il > 0 && jl > 0)
result_k += response_partial[k + 3 * (jl - 1) + 3 * _filter_dim_j * (il - 1)];
if (il > 0)
result_k -= response_partial[k + 3 * (jh - 1) + 3 * _filter_dim_j * (il - 1)];
if (jl > 0)
result_k -= response_partial[k + 3 * (jl - 1) + 3 * _filter_dim_j * (ih - 1)];
result_k += response_partial[k + 3 * (jh - 1) + 3 * _filter_dim_j * (ih - 1)];
result.set_matrix(k, k, result_k);
}
return result;
}
public:
virtual unsigned int varieties() const = 0;
virtual unsigned int select(unsigned int i, unsigned int j) const = 0;
/*
* Get a specific pixel response.
*/
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig, unsigned int variety,
char channels) const {
assert (variety < num_arrays);
ale_real *response_partial = response_partials[variety];
assert (response_partial != NULL);
return generic_response(response_partial, top, bot, lef, rig, channels);
}
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig,
unsigned int variety) const {
return operator()(top, bot, lef, rig, variety, 0x7);
}
#if 0
/*
* Get the average pixel response.
*/
psf_result operator()(float top, float bot, float lef, float rig) const {
return generic_response(avg_response, top, bot, lef, rig);
}
#endif
protected:
/*
* Integrate over the whole PSF
*/
pixel integrate(ale_real *response_array) {
pixel result;
for (unsigned int i = 0; i < _filter_dim_i; i++)
for (unsigned int j = 0; j < _filter_dim_j; j++)
for (unsigned int k = 0; k < 3 ; k++)
result[k] += response_array[i * _filter_dim_j * 3 + j * 3 + k];
for (unsigned int k = 0; k < 3; k++)
result[k] *= (((ale_real) 4 * _height * _width)
/ (ale_real) (_filter_dim_i * _filter_dim_j));
return result;
}
void partial_integrate(ale_real *target, ale_real *source) {
ale_real element_area = (ale_real) (max_i() - min_i())
* (ale_real) (max_j() - min_j())
/ (ale_real) (_filter_dim_i)
/ (ale_real) (_filter_dim_j);
for (unsigned int i = 0; i < _filter_dim_i; i++)
for (unsigned int j = 0; j < _filter_dim_j; j++)
for (unsigned int k = 0; k < 3 ; k++) {
unsigned int index = i * _filter_dim_j * 3 + j * 3 + k;
target[index] = source[index] * element_area
+ ((j > 0) ? target[index - 3] : ale_real_0)
+ ((i > 0) ? target[index - _filter_dim_j * 3] : ale_real_0)
- ((i > 0
&& j > 0) ? target[index - _filter_dim_j * 3 - 3] : ale_real_0);
}
}
/*
* Compute integrals.
*/
void compute_integrals() {
response_integrals = new pixel[num_arrays];
for (unsigned int n = 0; n < num_arrays; n++)
response_integrals[n] = integrate(response_arrays[n]);
#if 0
avg_integral = integrate(avg_response);
#endif
response_partials = (ale_real **) malloc(sizeof(ale_real *) * num_arrays);
assert(response_partials);
for (unsigned int n = 0; n < num_arrays; n++) {
response_partials[n] = (ale_real *) malloc(sizeof(ale_real) * _filter_dim_i
* _filter_dim_j
* 3);
assert(response_partials[n]);
partial_integrate(response_partials[n], response_arrays[n]);
}
}
public:
/*
* Return elements of given integrals
*/
pixel integral(unsigned int n) const {
assert (response_integrals != NULL);
return response_integrals[n];
}
#if 0
pixel integral() const {
return avg_integral;
}
#endif
raster () {
response_integrals = NULL;
}
virtual ~raster() {
/*
* Deallocate data structures.
*/
for (unsigned int n = 0; n < num_arrays; n++)
free(response_arrays[n]);
free(response_arrays);
#if 0
free(avg_response);
#endif
if (response_integrals)
delete response_integrals;
}
};
#endif

192
d2/render/psf/rasterizer.h Normal file
View File

@@ -0,0 +1,192 @@
// 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
*/
#ifndef __psf_rasterize_h__
#define __psf_rasterize_h__
#include "../../point.h"
#include "raster.h"
#include "psf.h"
/*
* Point-spread function rasterizer.
*
* These operations rasterize a PSF to a multiple of the resolution of the
* rendering grid for a given frame.
*/
class rasterizer : public raster {
psf *input;
public:
unsigned int varieties() const {
return input->varieties();
}
unsigned int select(unsigned int i, unsigned int j) const {
return input->select(i, j);
}
rasterizer (psf *input, transformation t) {
this->input = input;
_height = -input->min_i();
if (input->max_i() > _height)
_height = input->max_i();
_width = -input->min_j();
if (input->max_j() > _width)
_width = input->max_j();
/*
* Approximate the desired resolution.
*
* Assume that maximum resolution is reached at (at least) one
* of the corners of the image. (This should be true for
* projective, Euclidean, and translational transformations,
* but it would be worthwhile to check/prove this for the
* projective case at some point, since it's a bit less
* obvious.)
*/
point min_diff;
/*
* XXX: this loop breaks when height <= 1 or width <= 1.
*/
for (unsigned int i = 0; i < t.unscaled_height(); i += (t.unscaled_height() - 1))
for (unsigned int j = 0; j < t.unscaled_width(); j += (t.unscaled_width() - 1)) {
point corner = point(i, j);
point delta1 = corner - t.scaled_inverse_transform(t.transform_scaled(corner) + point(1, 0));
point delta2 = corner - t.scaled_inverse_transform(t.transform_scaled(corner) + point(0, 1));
for (int index = 0; index < 2; index++) {
ale_pos d1 = fabs(delta1[index]);
ale_pos d2 = fabs(delta2[index]);
/*
* Take the largest change in each direction.
*/
ale_pos delta = (d1 > d2) ? d1 : d2;
if ((i == 0 && j == 0) || delta < min_diff[index])
min_diff[index] = delta;
}
}
ale_real resolution_multiplier = 20; /* Arbitrary */
_filter_dim_i = (int) ceil((ale_real) 2 * _height * resolution_multiplier / (ale_real) min_diff[0]);
_filter_dim_j = (int) ceil((ale_real) 2 * _width * resolution_multiplier / (ale_real) min_diff[1]);
/*
* Ensure that the array has an odd number of elements in each
* direction. This allows us to move the center to the right
* place when using FFTW.
*/
if (_filter_dim_i % 2 == 0)
_filter_dim_i++;
if (_filter_dim_j % 2 == 0)
_filter_dim_j++;
/*
* Determine the number of arrays to create.
*/
num_arrays = input->varieties();
/*
* Create arrays
*/
response_arrays = (ale_real **)malloc(num_arrays * sizeof(ale_real *));
if (!response_arrays) {
fprintf(stderr, "Could not allocate in rasterizer.\n");
exit(1);
}
ale_real stepsize_i = (2 * _height) / _filter_dim_i;
ale_real stepsize_j = (2 * _width) / _filter_dim_j;
ale_real divisor = stepsize_i * stepsize_j;
for (unsigned int n = 0; n < num_arrays; n++) {
response_arrays[n] = (ale_real *)malloc(_filter_dim_i * _filter_dim_j * 3
* sizeof(ale_real));
if (!response_arrays[n]) {
fprintf(stderr, "Could not allocate in rasterizer.\n");
exit(1);
}
for (unsigned int i = 0; i < _filter_dim_i; i++)
for (unsigned int j = 0; j < _filter_dim_j; j++) {
psf_result r
= (*input)(-_height + stepsize_i * (ale_real) i,
-_height + stepsize_i * (ale_real) (i + 1),
-_width + stepsize_j * (ale_real) j,
-_width + stepsize_j * (ale_real) (j + 1), n);
for (unsigned int k = 0; k < 3; k++) {
response_arrays[n][i * _filter_dim_j * 3 + j * 3 + k]
= r.matrix(k, k) / divisor;
}
}
}
#if 0
avg_response = (ale_real *)malloc(_filter_dim_i * _filter_dim_j * 3
* sizeof(ale_real));
if (!avg_response) {
fprintf(stderr, "Could not allocate in rasterizer.\n");
exit(1);
}
for (unsigned int i = 0; i < _filter_dim_i; i++)
for (unsigned int j = 0; j < _filter_dim_j; j++) {
psf::psf_result r
= (*input)(-_height + stepsize_i * i,
-_height + stepsize_i * (i + 1),
-_width + stepsize_j * j,
-_width + stepsize_j * (j + 1));
for (unsigned int k = 0; k < 3; k++)
avg_response[i * _filter_dim_j * 3 + j * 3 + k]
= r.matrix(k, k) / divisor;
}
#endif
compute_integrals();
// fprintf(stderr, "(w=%f h=%f we=%d he=%d [", _width, _height, _filter_dim_j, _filter_dim_i);
// for (unsigned int i = 0; i < _filter_dim_i; i++)
// for (unsigned int j = 0; j < _filter_dim_j; j++)
// fprintf(stderr, "%f ", response_arrays[0][i * _filter_dim_j * 3 + j * 3 + 0]);
// fprintf(stderr, "])");
}
};
#endif

101
d2/render/psf/scalar_mult.h Normal file
View File

@@ -0,0 +1,101 @@
// Copyright 2003, 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 __psf_scalar_mult_h__
#define __psf_scalar_mult_h__
#include "../../point.h"
#include "psf.h"
/*
* Point-spread function module.
*
* This module implements the scalar_mult (f1 * f2) of point-spread functions f1 and
* f2.
*/
class scalar_mult : public psf {
ale_pos _radius;
psf *f;
ale_real scalar;
ale_real _min_i, _max_i, _min_j, _max_j;
public:
/*
* The following four functions indicate filter boundaries. Filter
* support may include everything up to and including the boundaries
* specified here.
*/
ale_real min_i() const { return _min_i; }
ale_real max_i() const { return _max_i; }
ale_real min_j() const { return _min_j; }
ale_real max_j() const { return _max_j; }
/*
* Get the number of varieties supported by this PSF. These usually
* correspond to different points in the sensor array.
*/
virtual unsigned int varieties() {
return f->varieties();
}
/*
* Select the variety appropriate for a given position in the sensor
* array.
*/
virtual unsigned int select(unsigned int i, unsigned int j) {
return f->select(i, j);
}
/*
* Response function
*
* Get the response to the rectangle bounded by (top, bot, lef, rig).
* This function must correctly handle points which fall outside of the
* filter support. The variety of the responding pixel is provided, in
* case response is not uniform for all pixels (e.g. some sensor arrays
* stagger red, green, and blue sensors).
*/
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig,
unsigned int variety) const {
psf_result result;
psf_result r;
r = (*f)(top, bot, lef, rig, variety);
for (int k1 = 0; k1 < 3; k1++)
for (int k2 = 0; k2 < 3; k2++)
result.set_matrix(k1, k2, scalar * r.get_matrix(k1, k2));
return result;
}
scalar_mult(ale_real s, psf *f) {
this->scalar = s;
this->f = f;
_min_i = f->min_i();
_min_j = f->min_j();
_max_i = f->max_i();
_max_j = f->max_j();
}
};
#endif

186
d2/render/psf/stdin.h Normal file
View File

@@ -0,0 +1,186 @@
// 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
*/
#ifndef __psf_stdin_h__
#define __psf_stdin_h__
#include "../../point.h"
#include "psf.h"
/*
* Point-spread function module.
*
* This response function is configured by input from stdin. A series of
* prompts indicates the information required.
*/
class psf_stdin : public psf {
ale_real _height;
ale_real _width;
int _filter_dim_i;
int _filter_dim_j;
ale_real *response_array;
public:
/*
* The following four functions indicate filter boundaries. Filter
* support may include everything up to and including the boundaries
* specified here.
*/
ale_real min_i() const { return -_height; }
ale_real max_i() const { return _height; }
ale_real min_j() const { return -_width; }
ale_real max_j() const { return _width; }
/*
* Response functions
*
* response_generic() and operator()()
*
* Get the response to the rectangle bounded by (top, bot, lef, rig).
* This function must correctly handle points which fall outside of the
* filter support. The variety of the responding pixel is provided, in
* case response is not uniform for all pixels (e.g. some sensor arrays
* stagger red, green, and blue sensors).
*/
psf_result response_generic(ale_real *response_array, ale_real top, ale_real bot,
ale_real lef, ale_real rig) const {
assert (response_array != NULL);
psf_result result;
if (top < min_i())
top = min_i();
if (bot > max_i())
bot = max_i();
if (lef < min_j())
lef = min_j();
if (rig > max_j())
rig = max_j();
int il = (int) floor((top - min_i()) / (max_i() - min_i()) * _filter_dim_i);
int ih = (int) floor((bot - min_i()) / (max_i() - min_i()) * _filter_dim_i);
int jl = (int) floor((lef - min_j()) / (max_j() - min_j()) * _filter_dim_j);
int jh = (int) floor((rig - min_j()) / (max_j() - min_j()) * _filter_dim_j);
// fprintf(stderr, "(il, ih, jl, jh) = (%d, %d, %d, %d)\n", il, ih, jl, jh);
for (int ii = il; ii <= ih; ii++)
for (int jj = jl; jj <= jh; jj++) {
ale_real ltop = ((ale_real) ii) / _filter_dim_i * (max_i() - min_i()) + min_i();
ale_real lbot = ((ale_real) ii + 1) / _filter_dim_i * (max_i() - min_i()) + min_i();
ale_real llef = ((ale_real) jj) / _filter_dim_j * (max_j() - min_j()) + min_j();
ale_real lrig = ((ale_real) jj + 1) / _filter_dim_j * (max_j() - min_j()) + min_j();
if (ltop < top)
ltop = top;
if (lbot > bot)
lbot = bot;
if (llef < lef)
llef = lef;
if (lrig > rig)
lrig = rig;
for (int k = 0; k < 3; k++) {
result.matrix(k, k) += (ale_real) ((lbot - ltop) * (lrig - llef)
* response_array[3 * _filter_dim_j * ii + 3 * jj + k]);
}
}
return result;
}
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real
rig, unsigned int variety) const {
return response_generic(response_array, top, bot, lef, rig);
}
void class_error() {
fprintf(stderr, "\n\nALE Panic: Error acquiring input. Exiting.\n");
exit(1);
}
psf_stdin () {
printf("\nEnter filter support height, in units of pixels (e.g. 2.5): ");
fflush(stdout);
double dheight;
if (scanf("%lf", &dheight) != 1) {
class_error();
}
_height = dheight / 2;
printf("\nEnter filter support width, in units of pixels (e.g. 2.5): ");
fflush(stdout);
double dwidth;
if (scanf("%lf", &dwidth) != 1) {
class_error();
}
_width = dwidth / 2;
printf("\nEnter the number of rows in the filter (e.g. 3): ");
fflush(stdout);
if (scanf("%d", &_filter_dim_i) != 1 || _filter_dim_i < 1) {
class_error();
}
printf("\nEnter the number of columns in the filter (e.g. 3): ");
fflush(stdout);
if (scanf("%d", &_filter_dim_j) != 1 || _filter_dim_j < 1) {
class_error();
}
response_array = (ale_real *) malloc(_filter_dim_i * _filter_dim_j * 3
* sizeof(ale_real));
if (response_array == NULL) {
fprintf(stderr, "\n\nCould not allocate filter.\n");
exit(1);
}
printf("\nFilter elements are labeled as (row, column, channel). The red channel of\n");
printf("the top-left element is (0, 0, 0), and the blue channel of the bottom-right\n");
printf("element is (%d, %d, 2).\n\n", _filter_dim_i - 1, _filter_dim_j - 1);
for (int i = 0; i < _filter_dim_i; i++)
for (int j = 0; j < _filter_dim_j; j++)
for (int k = 0; k < 3; k++) {
printf("Enter value for element (%d, %d, %d) (e.g. 2.5): ",
i, j, k);
fflush(stdout);
double delem;
if (scanf("%lf", &delem) != 1)
class_error();
response_array[i * _filter_dim_j * 3 + j * 3 + k] = delem;
}
}
virtual ~psf_stdin() {
/*
* Don't free this without creating a copy constructor.
*/
free(response_array);
}
};
#endif

224
d2/render/psf/stdin_vg.h Normal file
View File

@@ -0,0 +1,224 @@
// 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
*/
#ifndef __psf_stdin_vg_h__
#define __psf_stdin_vg_h__
#include "../../point.h"
#include "psf.h"
/*
* Point-spread function module.
*
* This response function is configured by input from stdin. A series of
* prompts indicates the information required.
*/
class psf_stdin_vg : public psf {
ale_real _height;
ale_real _width;
ale_real gap_width;
int _filter_dim_i;
int _filter_dim_j;
ale_real *response_array;
public:
/*
* The following four functions indicate filter boundaries. Filter
* support may include everything up to and including the boundaries
* specified here.
*/
ale_real min_i() const { return -_height; }
ale_real max_i() const { return _height; }
ale_real min_j() const { return -_width - fabs(gap_width); }
ale_real max_j() const { return _width + fabs(gap_width); }
/*
* Get the number of varieties supported by this PSF. These usually
* correspond to different points in the sensor array.
*/
virtual unsigned int varieties() const {
return 8;
}
/*
* Select the variety appropriate for a given position in the sensor
* array.
*/
virtual unsigned int select(unsigned int i, unsigned int j) {
return (j % 8);
}
/*
* Response functions
*
* response_generic() and operator()()
*
* Get the response to the rectangle bounded by (top, bot, lef, rig).
* This function must correctly handle points which fall outside of the
* filter support. The variety of the responding pixel is provided, in
* case response is not uniform for all pixels (e.g. some sensor arrays
* stagger red, green, and blue sensors).
*/
psf_result response_generic(ale_real *response_array, ale_real top, ale_real bot,
ale_real lef, ale_real rig, unsigned int variety) const {
assert (response_array != NULL);
assert (variety < varieties());
psf_result result;
ale_pos offset = (gap_width / 2)
- (gap_width / 7) * variety;
lef -= offset;
rig -= offset;
if (top < min_i())
top = min_i();
if (bot > max_i())
bot = max_i();
if (lef < -_width)
lef = -_width;
if (rig > _width)
rig = _width;
int il = (int) floor((top - min_i()) / (max_i() - min_i()) * _filter_dim_i);
int ih = (int) floor((bot - min_i()) / (max_i() - min_i()) * _filter_dim_i);
int jl = (int) floor((lef + _width) / (_width * 2) * _filter_dim_j);
int jh = (int) floor((rig + _width) / (_width * 2) * _filter_dim_j);
// fprintf(stderr, "(il, ih, jl, jh) = (%d, %d, %d, %d)\n", il, ih, jl, jh);
for (int ii = il; ii <= ih; ii++)
for (int jj = jl; jj <= jh; jj++) {
ale_real ltop = ((ale_real) ii) / (ale_real) _filter_dim_i * (max_i() - min_i()) + min_i();
ale_real lbot = ((ale_real) ii + 1) / (ale_real) _filter_dim_i * (max_i() - min_i()) + min_i();
ale_real llef = ((ale_real) jj) / (ale_real) _filter_dim_j * ((ale_real) _width * (ale_real) 2) - _width;
ale_real lrig = ((ale_real) jj + 1) / (ale_real) _filter_dim_j * (_width * (ale_real) 2) - _width;
if (ltop < top)
ltop = top;
if (lbot > bot)
lbot = bot;
if (llef < lef)
llef = lef;
if (lrig > rig)
lrig = rig;
for (int k = 0; k < 3; k++) {
result.matrix(k, k) += (ale_real) ((lbot - ltop) * (lrig - llef)
* response_array[3 * _filter_dim_j * ii + 3 * jj + k]);
}
}
return result;
}
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real
rig, unsigned int variety) const {
return response_generic(response_array, top, bot, lef, rig, variety);
}
#if 0
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig) const {
return response_generic(response_array, top, bot, lef, rig, 4);
}
#endif
void class_error() {
fprintf(stderr, "\n\nALE Panic: Error acquiring input. Exiting.\n");
exit(1);
}
psf_stdin_vg () {
printf("\nEnter vertical gap width, in units of pixels (e.g. 1.0): ");
fflush(stdout);
double dgap_width;
if (scanf("%lf", &dgap_width) != 1) {
class_error();
}
gap_width = dgap_width;
printf("\nEnter filter support height, in units of pixels (e.g. 2.5): ");
fflush(stdout);
double dheight;
if (scanf("%lf", &dheight) != 1) {
class_error();
}
_height = dheight / 2;
printf("\nEnter filter support width, in units of pixels (e.g. 2.5): ");
fflush(stdout);
double dwidth;
if (scanf("%lf", &dwidth) != 1) {
class_error();
}
_width = dwidth / 2;
printf("\nEnter the number of rows in the filter (e.g. 3): ");
fflush(stdout);
if (scanf("%d", &_filter_dim_i) != 1 || _filter_dim_i < 1) {
class_error();
}
printf("\nEnter the number of columns in the filter (e.g. 3): ");
fflush(stdout);
if (scanf("%d", &_filter_dim_j) != 1 || _filter_dim_j < 1) {
class_error();
}
response_array = (ale_real *) malloc(_filter_dim_i * _filter_dim_j * 3
* sizeof(ale_real));
if (response_array == NULL) {
fprintf(stderr, "\n\nCould not allocate filter.\n");
exit(1);
}
printf("\nFilter elements are labeled as (row, column, channel). The red channel of\n");
printf("the top-left element is (0, 0, 0), and the blue channel of the bottom-right\n");
printf("element is (%d, %d, 2).\n\n", _filter_dim_i - 1, _filter_dim_j - 1);
for (int i = 0; i < _filter_dim_i; i++)
for (int j = 0; j < _filter_dim_j; j++)
for (int k = 0; k < 3; k++) {
printf("Enter value for element (%d, %d, %d) (e.g. 2.5): ",
i, j, k);
fflush(stdout);
double delem;
if (scanf("%lf", &delem) != 1)
class_error();
response_array[i * _filter_dim_j * 3 + j * 3 + k] = delem;
}
}
virtual ~psf_stdin_vg() {
/*
* Don't free this without creating a copy constructor.
*/
free(response_array);
}
};
#endif

131
d2/render/psf/sum.h Normal file
View File

@@ -0,0 +1,131 @@
// 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
*/
#ifndef __psf_sum_h__
#define __psf_sum_h__
#include "../../point.h"
#include "psf.h"
/*
* Point-spread function module.
*
* This module implements the sum (f1 + f2) of point-spread functions f1 and
* f2. This is not a convolution.
*/
class sum : public psf {
ale_pos _radius;
psf *f1, *f2;
ale_real _min_i, _max_i, _min_j, _max_j;
public:
/*
* The following four functions indicate filter boundaries. Filter
* support may include everything up to and including the boundaries
* specified here.
*/
ale_real min_i() const { return _min_i; }
ale_real max_i() const { return _max_i; }
ale_real min_j() const { return _min_j; }
ale_real max_j() const { return _max_j; }
/*
* Get the number of varieties supported by this PSF. These usually
* correspond to different points in the sensor array.
*/
virtual unsigned int varieties() {
return f1->varieties() * f2->varieties();
}
/*
* Select the variety appropriate for a given position in the sensor
* array.
*/
virtual unsigned int select(unsigned int i, unsigned int j) {
return (f1->select(i, j) * f2->varieties() + f2->select(i, j));
}
/*
* Response function
*
* Get the response to the rectangle bounded by (top, bot, lef, rig).
* This function must correctly handle points which fall outside of the
* filter support. The variety of the responding pixel is provided, in
* case response is not uniform for all pixels (e.g. some sensor arrays
* stagger red, green, and blue sensors).
*/
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig,
unsigned int variety) const {
psf_result result;
psf_result r1, r2;
r1 = (*f1)(top, bot, lef, rig, variety / f2->varieties());
r2 = (*f2)(top, bot, lef, rig, variety % f2->varieties());
for (int k1 = 0; k1 < 3; k1++)
for (int k2 = 0; k2 < 3; k2++)
result.set_matrix(k1, k2, r1.get_matrix(k1, k2)
+ r2.get_matrix(k1, k2));
return result;
}
#if 0
/*
* Get the average pixel response.
*/
psf_result operator()(ale_real top, ale_real bot, ale_real lef, ale_real rig) {
psf_result result;
psf_result r1, r2;
r1 = (*f1)(top, bot, lef, rig);
r2 = (*f2)(top, bot, lef, rig);
for (int k1 = 0; k1 < 3; k1++)
for (int k2 = 0; k2 < 3; k2++)
result.set_matrix(k1, k2, r1.get_matrix(k1, k2)
+ r2.get_matrix(k1, k2));
return result;
}
#endif
sum(psf *f1, psf *f2) {
this->f1 = f1;
this->f2 = f2;
_min_i = f1->min_i();
_min_j = f1->min_j();
_max_i = f1->max_i();
_max_j = f1->max_j();
if (_min_i > f2->min_i())
_min_i = f2->min_i();
if (_min_j > f2->min_j())
_min_j = f2->min_j();
if (_max_i < f2->max_i())
_max_i = f2->max_i();
if (_max_j < f2->max_j())
_max_j = f2->max_j();
}
};
#endif

218
d2/render/usm.h Normal file
View File

@@ -0,0 +1,218 @@
// 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
*/
/*
* usm.h: A render subclass that implements an unsharp mask
* postprocessing algorithm.
*/
#ifndef __usm_h__
#define __usm_h__
#include "../image.h"
#include "../render.h"
#include "psf/psf.h"
class usm : public render {
int done;
int inc;
image *done_image;
render *input;
const image *input_image;
const image *input_defined;
ale_real scale_factor;
ale_real usm_multiplier;
psf *lresponse, *nlresponse;
const exposure *exp;
/*
* USM value for point (i, j).
*/
pixel _usm(int i, int j, const image *im) {
pixel result;
ale_real d = scale_factor / 2;
pixel weight;
/*
* Convolve with the linear filter, iterating over pixels
* according to the filter support, and tracking contribution
* weights in the variable WEIGHT.
*/
for (int ii = (int) floor(i - d + lresponse->min_i());
ii <= ceil(i + d + lresponse->max_i()); ii++)
for (int jj = (int) floor(j - d + lresponse->min_j());
jj <= ceil(j + d + lresponse->max_j()); jj++) {
ale_real top = ii - d;
ale_real bot = ii + d;
ale_real lef = jj - d;
ale_real rig = jj + d;
if (ii >= (int) 0
&& ii < (int) im->height()
&& jj >= (int) 0
&& jj < (int) im->width()
&& input_defined->get_pixel(ii, jj)[0]) {
class psf::psf_result r = (*lresponse)((top - i) / scale_factor,
(bot - i) / scale_factor,
(lef - j) / scale_factor,
(rig - j) / scale_factor);
if (nlresponse) {
/*
* Convolve with the non-linear filter,
* iterating over pixels according to the
* filter support, and tracking contribution
* weights in the variable WWEIGHT.
*
* Note: This approach is efficient
* space-wise, but inefficient timewise. There
* is probably a better approach to this.
*/
pixel rresult(0, 0, 0), wweight(0, 0, 0);
for (int iii = (int) floor(ii - d + nlresponse->min_i());
iii <= ceil(ii + d + lresponse->max_i()); iii++)
for (int jjj = (int) floor(jj - d + nlresponse->min_j());
jjj <= ceil(jj + d + lresponse->max_j()); jjj++) {
ale_real top = iii - d;
ale_real bot = iii + d;
ale_real lef = jjj - d;
ale_real rig = jjj + d;
if (iii >= (int) 0
&& iii < (int) im->height()
&& jjj >= (int) 0
&& jjj < (int) im->width()
&& input_defined->get_pixel(iii, jjj)[0]) {
class psf::psf_result r = (*nlresponse)((top - ii) / scale_factor,
(bot - ii) / scale_factor,
(lef - jj) / scale_factor,
(rig - jj) / scale_factor);
wweight += r.weight();
rresult += r(exp->unlinearize(im->get_pixel(iii, jjj)));
}
}
result += r(exp->linearize(rresult / wweight));
} else {
result += r(im->get_pixel(ii, jj));
}
weight += r.weight();
}
}
result /= weight;
result = im->get_pixel(i, j) - result;
if (finite(result[0])
&& finite(result[1])
&& finite(result[2]))
return result;
else
return pixel(0, 0, 0);
}
void _filter() {
assert (done_image->height() == input_image->height());
assert (done_image->width() == input_image->width());
assert (done_image->depth() == input_image->depth());
for (unsigned int i = 0; i < done_image->height(); i++)
for (unsigned int j = 0; j < done_image->width(); j++) {
if (!input_defined->get_pixel(i, j)[0])
continue;
done_image->set_pixel(i, j,
input_image->get_pixel(i, j)
+ usm_multiplier
* _usm(i, j, input_image));
}
}
public:
usm(render *input, ale_real scale_factor, ale_real usm_multiplier, int _inc,
psf *lresponse, psf *nlresponse, exposure *exp) {
this->input = input;
done = 0;
inc = _inc;
this->scale_factor = scale_factor;
this->usm_multiplier = usm_multiplier;
this->lresponse = lresponse;
this->nlresponse = nlresponse;
this->exp = exp;
}
const image *get_image() {
if (done)
return done_image;
else
return input->get_image();
}
const image *get_defined() {
return input->get_defined();
}
void sync(int n) {
render::sync(n);
input->sync(n);
}
void step() {
}
int sync() {
input->sync();
fprintf(stderr, "Applying USM");
done = 1;
done_image = input->get_image()->clone("USM done_image");
input_image = input->get_image();
input_defined = input->get_defined();
_filter();
if (inc)
image_rw::output(done_image);
fprintf(stderr, ".\n");
return 0;
}
virtual ~usm() {
}
};
#endif

74
d2/render/zero.h Normal file
View File

@@ -0,0 +1,74 @@
// 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
*/
/*
* zero.h: A renderer for the zero filter.
*/
#ifndef __render_zero_h__
#define __render_zero_h__
#include "incremental.h"
#include "../image_zero.h"
class zero : public incremental {
public:
zero(invariant *inv) : incremental(inv) {
assert (typeid(*inv->ssfe()->get_scaled_filter()->get_filter()) == typeid(filter::zero));
}
/*
* Perform the current rendering step.
*/
virtual void step() {
assert (get_step() >= -1);
if (get_step() == 0) {
transformation t = align::of(0);
const image *im = image_rw::open(0);
/*
* XXX: This approach to trimming pixels is probably a
* bit too aggressive. If it is changed, be sure to
* also change the corresponding lines in
* incremental.h.
*/
unsigned int trim_size = (int) ceil(get_scale_factor()) - 1;
accum_image = new image_zero((int) floor(im->height() * get_scale_factor()) - trim_size,
(int) floor(im->width() * get_scale_factor()) - trim_size, 3);
set_extents_by_map(0, t);
image_rw::close(0);
} else if (align::match(get_step())) {
transformation t = align::of(get_step());
if (is_extend())
increase_extents_by_map(get_step(), t);
}
}
void free_memory() {
}
};
#endif

222
d2/render_parse.h Normal file
View File

@@ -0,0 +1,222 @@
// 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
*/
#ifndef __render_parse_h__
#define __render_parse_h__
#include "render.h"
#include "render/combine.h"
#include "render/invariant.h"
#include "render/incremental.h"
#include "render/zero.h"
/*
* Parse strings describing renderers, and return a renderer satisfying
* the string.
*/
class render_parse {
private:
static int strpfix(const char *a, const char *b) {
return strncmp(a, b, strlen(a));
}
static void nomem() {
fprintf(stderr, "\n\n*** Error: unable to allocate memory in render_parse. ***\n\n");
exit(1);
}
static void syntax_error(const char *explanation) {
fprintf(stderr, "\n\n*** Error: Render syntax: %s ***\n\n", explanation);
exit(1);
}
static filter::filter *get_SF_atomic(const char *type) {
double param;
if (!strcmp("sinc", type)) {
return new filter::sinc();
} else if (!strpfix("lanc:", type)) {
if (sscanf(type + strlen("lanc:"), "%lf", &param) != 1)
syntax_error("Unable to get lanczos diameter.");
return new filter::lanczos(param/2);
} else if (!strpfix("triangle:", type)) {
if (sscanf(type + strlen("triangle:"), "%lf", &param) != 1)
syntax_error("Unable to get triangle diameter.");
return new filter::triangle(param/2);
} else if (!strpfix("box:", type)) {
if (sscanf(type + strlen("box:"), "%lf", &param) != 1)
syntax_error("Unable to get box diameter.");
return new filter::box(param/2);
} else if (!strpfix("gauss:", type)) {
if (sscanf(type + strlen("gauss:"), "%lf", &param) != 1)
syntax_error("Unable to get gauss deviation.");
return new filter::gauss(param);
} else if (!strpfix("zero", type)) {
return new filter::zero();
} else {
fprintf(stderr, "get_SF_atomic type %s\n", type);
syntax_error("Unable to get filter.");
}
assert (0);
/*
* This line should never be reached; it's included to avoid
* 'unreachable' messages emitted by some compilers.
*/
return NULL;
}
static filter::filter *get_SF(const char *orig_type) {
char *type = strdup(orig_type);
if (type == NULL)
nomem();
char *star_index = (char *) type;
while (*star_index != '\0'
&& *star_index != '*')
star_index++;
if (*star_index == '\0') {
free(type);
return get_SF_atomic(orig_type);
}
*star_index = '\0';
filter::filter *result = new filter::mult(
get_SF_atomic(type),
get_SF(star_index + 1));
*star_index = '*';
free(type);
return result;
}
public:
static filter::scaled_filter *get_SSF(const char *type) {
if (!strpfix("coarse:", type)) {
return new filter::scaled_filter(get_SF(type + strlen("coarse:")), 1);
} else if (!strpfix("fine:", type)) {
return new filter::scaled_filter(get_SF(type + strlen("fine:")), 0);
} else if (!strpfix("auto:", type)) {
return new filter::scaled_filter(get_SF(type + strlen("auto:")), 2);
} else {
return new filter::scaled_filter(get_SF(type), 1);
}
}
static filter::ssfe *get_SSFE(const char *type) {
if (!strpfix("ex:", type)) {
return new filter::ssfe(get_SSF(type + strlen("ex:")), 1);
} else if (!strpfix("nex:", type)) {
return new filter::ssfe(get_SSF(type + strlen("nex:")), 0);
} else {
return new filter::ssfe(get_SSF(type), 1);
}
}
static render *get_invariant(const char *type) {
double param;
int offset;
invariant *i;
if (!strpfix("min:", type)) {
i = new invariant(get_SSFE(type + strlen("min:")));
i->set_min();
} else if (!strpfix("max:", type)) {
i = new invariant(get_SSFE(type + strlen("max:")));
i->set_max();
} else if (!strpfix("last:", type)) {
i = new invariant(get_SSFE(type + strlen("last:")));
i->set_last();
} else if (!strpfix("first:", type)) {
i = new invariant(get_SSFE(type + strlen("first:")));
i->set_first();
} else if (!strpfix("avg:", type)) {
i = new invariant(get_SSFE(type + strlen("avg:")));
i->set_avg();
} else if (!strpfix("avgf:", type)) {
if (sscanf(type + strlen("avgf:"), "%lf%n", &param, &offset) != 1)
syntax_error("Unable to get avgf weight criterion.");
i = new invariant(get_SSFE(type + strlen("avgf:") + offset + strlen(":")));
i->set_avgf(param);
} else if (!strpfix("median:", type)) {
i = new invariant(get_SSFE(type + strlen("median:")));
i->set_median();
} else {
i = new invariant(get_SSFE(type));
i->set_avg();
}
for (int index = 0; index < render::render_count(); index++)
if (typeid(*render::render_num(index)) == typeid(incremental)
&& i->equals(((incremental *)render::render_num(index))->get_invariant())) {
delete i;
return (incremental *)render::render_num(index);
} else if (typeid(*i->ssfe()->get_scaled_filter()->get_filter()) == typeid(filter::zero)) {
return new zero(i);
}
// fprintf(stderr, " new '%s'.\n", type);
return new incremental(i);
}
static render *get_combination(const char *ptype, const char *dtype) {
render *partial = get_invariant(ptype);
render *_default = get(dtype);
for (int index = 0; index < render::render_count(); index++)
if (typeid(*render::render_num(index)) == typeid(combine)
&& _default == ((combine *)render::render_num(index))->get_default()
&& partial == ((combine *)render::render_num(index))->get_partial()) {
return render::render_num(index);
}
// fprintf(stderr, " new '%s,%s'.\n", ptype, dtype);
return new combine(_default, partial);
}
static render *get(const char *orig_type) {
char *type = strdup(orig_type);
if (type == NULL)
nomem();
char *comma_index = (char *) type;
while (*comma_index != '\0'
&& *comma_index != ',')
comma_index++;
if (*comma_index == '\0') {
free(type);
return get_invariant(orig_type);
}
*comma_index = '\0';
render *result = get_combination(type, comma_index + 1);
*comma_index = ',';
free(type);
return result;
}
};
#endif

114
d2/spixel.h Normal file
View File

@@ -0,0 +1,114 @@
// 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
*/
#ifndef __spixel_h__
#define __spixel_h__
/*
* Structure to describe a pixel to be used in storage.
*/
class spixel {
private:
ale_sreal x[3];
public:
spixel() {
x[0] = 0;
x[1] = 0;
x[2] = 0;
}
spixel(pixel p) {
x[0] = p[0];
x[1] = p[1];
x[2] = p[2];
}
spixel(ale_sreal x0, ale_sreal x1, ale_sreal x2) {
x[0] = x0;
x[1] = x1;
x[2] = x2;
}
operator pixel() const {
pixel result;
result[0] = x[0];
result[1] = x[1];
result[2] = x[2];
return result;
}
const ale_sreal &operator[](unsigned int i) const {
#if 0
/*
* This may be expensive.
*/
assert (i < 3);
#endif
return x[i];
}
ale_sreal &operator[](unsigned int i) {
#if 0
/*
* This may be expensive.
*/
assert (i < 3);
#endif
return x[i];
}
ale_sreal min_norm() const {
ale_sreal m = x[0];
if (x[1] < m)
m = x[1];
if (x[2] < m)
m = x[2];
return m;
}
spixel operator+=(pixel p) {
return spixel(x[0] += p[0], x[1] += p[1], x[2] += p[2]);
}
spixel operator*=(pixel p) {
return spixel(x[0] *= p[0], x[1] *= p[1], x[2] *= p[2]);
}
spixel operator*=(ale_real d) {
return spixel(x[0] *= d, x[1] *= d, x[2] *= d);
}
spixel operator/=(pixel p) {
return spixel(x[0] /= p[0], x[1] /= p[1], x[2] /= p[2]);
}
spixel operator/=(ale_real d) {
return spixel(x[0] /= d, x[1] /= d, x[2] /= d);
}
};
#endif

28
d2/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;

917
d2/tfile.h Normal file
View File

@@ -0,0 +1,917 @@
// Copyright 2002, 2003, 2007 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
*/
/*
* tfile.h: Read and write transformation data files.
*/
/*
* This version of ALE reads transformation data file versions 0, 1, 2, and 3,
* and writes version 2 and 3 transformation data files. Data file versions 1
* and higher are identified by a version command "V x", where x is the version
* number, prior to any transformation command. Data file version 0 is
* identified by having no version command.
*/
#ifndef __tfile_h__
#define __tfile_h__
#include "transformation.h"
#define TFILE_VERSION 3
#define TFILE_VERSION_MAX 3
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;
pixel orig_apm;
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.
*
* 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 transformation tload_first(struct tload_t *t, int is_p,
transformation default_transform, int *is_default) {
transformation 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,
"\ntrans-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);
/*
* Check for version 0
*/
if (first_character != 'V')
/*
* Must be version 0.
*/
return result;
/*
* Obtain version from version command string.
*/
char line[1024];
fgets(line, 1024, t->file);
if (strlen(line) >= 1023) {
fprintf(stderr,
"\ntrans-load: Error: line too long in input file\n");
exit(1);
}
int count = sscanf(line, "V %d", &tfile_input_version);
if (count < 1) {
fprintf(stderr, "Error in transformation "
"file version command.\n");
exit(1);
} else if (tfile_input_version > TFILE_VERSION_MAX) {
fprintf(stderr, "Unsupported transformation "
"file version %d\n",
tfile_input_version);
exit(1);
}
/*
* Handle versions lower than 3.
*/
if (tfile_input_version < 3)
/*
* Versions lower than 3 use the default transformation
* for the original frame.
*/
return result;
/*
* 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);
}
switch (line[0]) {
case ' ':
case 0xa:
case 0xd:
case '\t':
case '#':
/* Comment or whitespace */
break;
case 'D':
case 'd':
/* Default transformation */
return result;
case 'B':
case 'b':
if (tfile_input_version < 3) {
fprintf(stderr, "\ntrans-load: Error: "
"Barrel distortion not supported "
"for version %d input files.\n"
"trans-load: Hint: Use version 3 "
"file syntax.\n", tfile_input_version);
exit(1);
} else {
unsigned int count;
unsigned int pos = 0, chars;
unsigned int bdc;
double dparameters[BARREL_DEGREE];
ale_pos parameters[BARREL_DEGREE];
count = sscanf(line, "B %u%n", &bdc, &chars);
pos += chars;
if (count < 1) {
fprintf(stderr, "\ntrans-load: Error: "
"Malformed 'B' command.\n");
exit(1);
}
if (bdc > result.bd_max()) {
fprintf(stderr, "\ntrans-load: Error: "
"Barrel distortion degree %d "
"is too large. (Maximum is %d.)\n"
"trans-load: Hint: "
"Reduce degree or re-compile "
"with BD_DEGREE=%d\n", bdc, BARREL_DEGREE, bdc);
exit(1);
}
for (unsigned int d = 0; d < bdc; d++) {
count = sscanf(line + pos, "%lf%n", &dparameters[d], &chars);
pos += chars;
if (count < 1) {
fprintf(stderr, "\ntrans-load: Error: "
"Malformed 'B' command.\n");
exit(1);
}
parameters[d] = dparameters[d];
}
result.bd_set(bdc, parameters);
}
break;
case 'P':
case 'p':
/* Projective transformation data */
*is_default = 0;
if (is_p == 0) {
fprintf(stderr, "\ntrans-load: Error: "
"Projective data for euclidean "
"transformation.\n"
"trans-load: Hint: "
"Use command-line option --projective.\n");
exit(1);
} else {
double width, height, values[8];
int count, i;
point x[4];
count = sscanf(line + 1, " %lf%lf%lf%lf%lf%lf%lf%lf%lf%lf", &width, &height,
&values[0], &values[1], &values[2], &values[3],
&values[4], &values[5], &values[6], &values[7]);
int index = 0;
for (int i = 0; i < 4; i++)
for (int j = 1; j >= 0; j--)
x[i][j] = values[index++];
if (count < 10)
fprintf(stderr, "\ntrans-load: warning:"
"Missing args for 'P'\n");
for (i = 0; i < count - 2; i++) {
ale_pos factor = (i % 2)
? ((double) result.scaled_width() / width)
: ((double) result.scaled_height() / height);
x[i / 2][i % 2] *= factor;
}
result.gpt_set(x);
return result;
}
break;
case 'E':
case 'e':
/* Euclidean transformation data */
*is_default = 0;
{
double width, height;
double values[3] = {0, 0, 0};
int count, i;
ale_pos eu[3];
count = sscanf(line + 1, " %lf%lf%lf%lf%lf",
&width, &height,
&values[0], &values[1], &values[2]);
eu[1] = values[0];
eu[0] = values[1];
eu[2] = values[2];
if (count < 5)
fprintf(stderr, "\ntrans-load: warning:"
"Missing args for 'E'\n");
for (i = 0; (i < count - 2) && (i < 2); i++) {
ale_pos factor = (i % 2)
? ((double) result.scaled_width() / width)
: ((double) result.scaled_height() / height);
eu[i] *= factor;
}
result.eu_set(eu);
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.
*
* IS_PRIMARY is used to differentiate primary and non-primary
* transformations
*/
static inline transformation tload_next(struct tload_t *t, int is_p,
transformation default_transform, int *is_default,
int is_primary) {
transformation result = default_transform;
*is_default = 1;
/*
* Read each line of the file until we find a transformation.
*/
while (t && !feof(t->file)) {
char c = fgetc(t->file);
if (!feof(t->file) && c != EOF)
ungetc(c, t->file);
if (feof(t->file)
|| (!is_primary
&& c != EOF
&& (c == 'E'
|| c == 'e'
|| c == 'P'
|| c == 'p'
|| c == 'D'
|| c == 'd'
|| c == 'B'
|| c == 'b'))) {
return result;
}
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");
}
switch (line[0]) {
case ' ':
case 0xa:
case 0xd:
case '\t':
case '#':
/* Comment or whitespace */
break;
case 'D':
case 'd':
/* Default transformation */
return result;
case 'B':
case 'b':
if (tfile_input_version < 3) {
fprintf(stderr, "\ntrans-load: Error: "
"Barrel distortion not supported "
"for version %d input files.\n"
"trans-load: Hint: Use version 3 "
"file syntax.\n", tfile_input_version);
exit(1);
} else {
unsigned int count;
unsigned int pos = 0, chars;
unsigned int bdc;
ale_pos parameters[BARREL_DEGREE];
double dparameters[BARREL_DEGREE];
count = sscanf(line, "B %u%n", &bdc, &chars);
pos += chars;
if (count < 1) {
fprintf(stderr, "\ntrans-load: Error: "
"Malformed 'B' command.\n");
exit(1);
}
if (bdc > result.bd_max()) {
fprintf(stderr, "\ntrans-load: Error: "
"Barrel distortion degree %d "
"is too large. (Maximum is %d.)\n"
"trans-load: Hint: "
"Reduce degree or re-compile "
"with BD_DEGREE=%d\n", bdc, BARREL_DEGREE, bdc);
exit(1);
}
for (unsigned int d = 0; d < bdc; d++) {
count = sscanf(line + pos, "%lf%n", &dparameters[d], &chars);
pos += chars;
if (count < 1) {
fprintf(stderr, "\ntrans-load: Error: "
"Malformed 'B' command.\n");
exit(1);
}
parameters[d] = dparameters[d];
}
result.bd_set(bdc, parameters);
}
break;
case 'Q':
case 'q':
if (is_primary)
break;
case 'P':
case 'p':
/* Projective transformation data */
if (is_p == 0) {
fprintf(stderr, "\ntrans-load: Error: "
"Projective data for euclidean "
"transformation.\n"
"trans-load: Hint: "
"Use command-line option --projective.\n");
exit(1);
} else {
double width, height, values[8];
int count, i;
point x[4];
transformation::multi_coordinate mc1, mc2;
count = sscanf(line + 1, " %lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%d%d%d", &width, &height,
&values[0], &values[1], &values[2], &values[3],
&values[4], &values[5], &values[6], &values[7],
&mc1.degree, &mc1.x, &mc1.y);
if (count == 13) {
mc2 = default_transform.get_current_coordinate();
if (mc1.degree < mc2.degree
|| mc1.degree == mc2.degree && mc1.y < mc2.y
|| mc1.degree == mc2.degree && mc1.y == mc2.y && mc1.x < mc2.x)
break;
if (mc1.degree != mc2.degree
|| mc1.x != mc2.x
|| mc1.y != mc2.y) {
if (!result.exists(mc1))
break;
result.set_current_index(result.get_index(mc1));
}
}
int index = 0;
for (int i = 0; i < 4; i++)
for (int j = 1; j >= 0; j--)
x[i][j] = values[index++];
if (count < 10)
fprintf(stderr, "\ntrans-load: warning:"
"Missing args for 'P'\n");
for (i = 0; i < count - 2; i++) {
ale_pos factor = (i % 2)
? ((double) result.scaled_width() / width)
: ((double) result.scaled_height() / height);
x[i / 2][i % 2] *= factor;
}
if (tfile_input_version < 1) {
/*
* Accommodate older versions
* of tfile.
*/
for (i = 0; i < 4; i++) {
ale_pos y = x[i][0];
x[i][0] = x[i][1];
x[i][1] = y;
}
result.gpt_v0_set(x);
} else {
result.gpt_set(x);
}
*is_default = 0;
return result;
}
break;
case 'F':
case 'f':
if (is_primary)
break;
case 'E':
case 'e':
/* Euclidean transformation data */
{
double width, height;
double values[3] = {0, 0, 0};
int count, i;
ale_pos eu[3];
transformation::multi_coordinate mc1, mc2;
count = sscanf(line + 1, " %lf%lf%lf%lf%lf%d%d%d",
&width, &height,
&values[0], &values[1], &values[2],
&mc1.degree, &mc1.x, &mc1.y);
if (count == 8) {
mc2 = default_transform.get_current_coordinate();
if (mc1.degree < mc2.degree
|| mc1.degree == mc2.degree && mc1.y < mc2.y
|| mc1.degree == mc2.degree && mc1.y == mc2.y && mc1.x < mc2.x)
break;
if (mc1.degree != mc2.degree
|| mc1.x != mc2.x
|| mc1.y != mc2.y) {
if (!result.exists(mc1))
break;
result.set_current_index(result.get_index(mc1));
}
}
eu[1] = values[0];
eu[0] = values[1];
eu[2] = values[2];
if (tfile_input_version < 2) {
ale_pos t = eu[0];
eu[0] = eu[1];
eu[1] = t;
}
if (count < 5)
fprintf(stderr, "\ntrans-load: warning:"
"Missing args for 'E'\n");
for (i = 0; (i < count - 2) && (i < 2); i++) {
ale_pos factor = (i % 2)
? ((double) result.scaled_width() / width)
: ((double) result.scaled_height() / height);
eu[i] *= factor;
}
if (tfile_input_version < 1) {
result.eu_v0_set(eu);
} else {
result.eu_set(eu);
}
*is_default = 0;
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 transformation file handler version %d\n",
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, transformation offset, int is_projective) {
if (t == NULL)
return;
t->file = fopen(t->filename, "a");
/*
* Determine the output version to use. We use version 3 output syntax only when
* necessary. This comprises two cases:
*
* (i) an input file is used, and this file uses version 3 syntax.
* (ii) non-degenerate barrel distortion correction is selected.
*
* (i) can be directly examined. When (i) does not hold, (ii) can be
* inferred from offset.bd_count(), since this value should be constant
* when (i) does not hold. XXX: This logic should be reviewed.
*/
if (tfile_input_version == 3 || offset.bd_count() > 0)
tfile_output_version = 3;
else
tfile_output_version = 2;
fprintf(t->file, "# producing transformation file syntax version %d\n", tfile_output_version);
fprintf(t->file, "V %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);
fprintf(t->file, "# Comment: Avg magnitude [r=%f g=%f b=%f]\n", (double) t->orig_apm[0], (double) t->orig_apm[1], (double) t->orig_apm[2]);
if (tfile_output_version < 3) {
fclose(t->file);
return;
}
if (offset.bd_count() > 0) {
assert (tfile_output_version >= 3);
unsigned int i;
fprintf(t->file, "B ");
fprintf(t->file, "%u ", offset.bd_count());
for (i = 0; i < offset.bd_count(); i++)
fprintf(t->file, "%f ", (double) offset.bd_get(i));
fprintf(t->file, "\n");
}
if (is_projective) {
int i, j;
fprintf(t->file, "P ");
fprintf(t->file, "%f %f ", (double) offset.scaled_width(), (double) offset.scaled_height());
for (i = 0; i < 4; i++)
for (j = 1; j >= 0; j--)
fprintf(t->file, "%f ", (double) offset.gpt_get(i, j));
} else {
fprintf(t->file, "E ");
fprintf(t->file, "%f %f ", (double) offset.scaled_width(), (double) offset.scaled_height());
fprintf(t->file, "%f ", (double) offset.eu_get(1));
fprintf(t->file, "%f ", (double) offset.eu_get(0));
fprintf(t->file, "%f ", (double) offset.eu_get(2));
}
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.
*
* IS_PRIMARY indicates whether to write a primary transformation
*
*/
static inline void tsave_next(struct tsave_t *t, transformation offset, int is_projective,
int is_primary) {
if (t == NULL)
return;
t->file = fopen(t->filename, "a");
if (is_primary && offset.bd_count() > 0) {
assert (tfile_output_version >= 3);
unsigned int i;
fprintf(t->file, "B ");
fprintf(t->file, "%u ", offset.bd_count());
for (i = 0; i < offset.bd_count(); i++)
fprintf(t->file, "%f ", (double) offset.bd_get(i));
fprintf(t->file, "\n");
}
if (is_projective) {
int i, j;
fprintf(t->file, is_primary ? "P " : "Q ");
fprintf(t->file, "%f %f ", (double) offset.scaled_width(), (double) offset.scaled_height());
for (i = 0; i < 4; i++)
for (j = 1; j >= 0; j--)
fprintf(t->file, "%f ", (double) offset.gpt_get(i, j));
} else {
fprintf(t->file, is_primary ? "E " : "F ");
fprintf(t->file, "%f %f ", (double) offset.scaled_width(), (double) offset.scaled_height());
fprintf(t->file, "%f ", (double) offset.eu_get(1));
fprintf(t->file, "%f ", (double) offset.eu_get(0));
fprintf(t->file, "%f ", (double) offset.eu_get(2));
}
if (!is_primary) {
transformation::multi_coordinate mc = offset.get_current_coordinate();
fprintf(t->file, "%d %d %d ", mc.degree, mc.x, mc.y);
}
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, pixel apm) {
if (t == NULL)
return;
t->orig = filename;
t->orig_apm = apm;
}
/*
* 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

648
d2/trans_abstract.h Normal file
View File

@@ -0,0 +1,648 @@
// Copyright 2002, 2004, 2007 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
*/
/*
* 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

28
d2/trans_multi.cc Normal file
View File

@@ -0,0 +1,28 @@
// Copyright 2008 David Hilvert <dhilvert@auricle.dyndns.org>
/* 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 "trans_multi.h"
/*
* See trans_multi.h for details on these variables.
*/
unsigned int trans_multi::_multi = 2;
ale_pos trans_multi::_multi_decomp = 100;
ale_pos trans_multi::_multi_improvement = 0;

1001
d2/trans_multi.h Normal file

File diff suppressed because it is too large Load Diff

840
d2/trans_single.h Normal file
View File

@@ -0,0 +1,840 @@
// 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
*/
/*
* trans_single.h: Represent transformations of the kind q = c(b^-1(p)),
* where p is a point in the source coordinate system, q is a point in the
* target coordinate system, b^-1 is a transformation correcting barrel
* distortion, and c is a transformation of projective or Euclidean type.
* (Note that ^-1 in this context indicates the function inverse rather than
* the exponential.)
*/
#ifndef __trans_single_h__
#define __trans_single_h__
#include "trans_abstract.h"
/*
* transformation: a structure to describe a transformation of kind q =
* c(b^-1(p)), where p is a point in the source coordinate system, q is a point
* in the target coordinate system, b^-1 is a transformation correcting barrel
* distortion, and c is a projective or Euclidean transformation. (Note that
* ^-1 in this case indicates a function inverse, not exponentiation.) Data
* elements are divided into those describing barrel distortion correction and
* those describing projective/Euclidean transformations.
*
* Barrel distortion correction estimates barrel distortion using polynomial
* functions of distance from the center of an image, following (roughly) the
* example set by Helmut Dersch in his PanoTools software:
*
* http://www.path.unimelb.edu.au/~dersch/barrel/barrel.html
*
* Projective transformation data member names roughly correspond to a typical
* treatment of projective transformations from:
*
* Heckbert, Paul. "Projective Mappings for Image Warping." Excerpted
* from his Master's Thesis (UC Berkeley, 1989). 1995.
*
* http://www.cs.cmu.edu/afs/cs/project/classes-ph/862.95/www/notes/proj.ps
*
* For convenience, Heckbert's 'x' and 'y' are noted here numerically by '0'
* and '1', respectively. 'x0' is denoted 'x[0][0]'; 'y0' is 'x[0][1]'.
*
* eu[i] are the parameters for euclidean transformations.
*
* We consider points to be transformed as homogeneous coordinate vectors
* multiplied on the right of the transformation matrix, and so we consider the
* transformation matrix as
*
* - -
* | a b c |
* | d e f |
* | g h i |
* - -
*
* where element i is always equal to 1.
*
*/
struct trans_single : public trans_abstract {
private:
point x[4];
ale_pos eu[3];
mutable ale_pos a, b, c, d, e, f, g, h; // matrix
mutable ale_pos _a, _b, _c, _d, _e, _f, _g, _h; // matrix inverse
int _is_projective;
pixel tonal_multiplier;
mutable int resultant_memo;
mutable int resultant_inverse_memo;
/*
* Calculate resultant matrix values.
*/
void resultant() const {
/*
* If we already know the answers, don't bother calculating
* them again.
*/
if (resultant_memo)
return;
int ale_pos_casting = ale_pos_casting_status();
ale_pos_enable_casting();
if (_is_projective) {
/*
* Calculate resultant matrix values for a general
* projective transformation given that we are mapping
* from the source domain of dimension input_height *
* input_width to a specified arbitrary quadrilateral.
* Follow the calculations outlined in the document by
* Paul Heckbert cited above for the case in which the
* source domain is a unit square and then divide to
* correct for the scale factor in each dimension.
*/
/*
* First, perform calculations as outlined in Heckbert.
*/
ale_pos delta_01 = x[1][0] - x[2][0];
ale_pos delta_02 = x[3][0] - x[2][0];
ale_pos sigma_0 = x[0][0] - x[1][0] + x[2][0] - x[3][0];
ale_pos delta_11 = x[1][1] - x[2][1];
ale_pos delta_12 = x[3][1] - x[2][1];
ale_pos sigma_1 = x[0][1] - x[1][1] + x[2][1] - x[3][1];
g = (sigma_0 * delta_12 - sigma_1 * delta_02)
/ (delta_01 * delta_12 - delta_11 * delta_02);
h = (delta_01 * sigma_1 - delta_11 * sigma_0 )
/ (delta_01 * delta_12 - delta_11 * delta_02);
a = (x[1][0] - x[0][0] + g * x[1][0]);
b = (x[3][0] - x[0][0] + h * x[3][0]);
c = x[0][0];
d = (x[1][1] - x[0][1] + g * x[1][1]);
e = (x[3][1] - x[0][1] + h * x[3][1]);
f = x[0][1];
/*
* Finish by scaling so that our transformation maps
* from a rectangle of width and height matching the
* width and height of the input image.
*/
a /= input_height;
b /= input_width;
d /= input_height;
e /= input_width;
g /= input_height;
h /= input_width;
} else {
/*
* Calculate matrix values for a euclidean
* transformation.
*
* We want to translate the image center by (eu[0],
* eu[1]) and rotate the image about the center by
* eu[2] degrees. This is equivalent to the following
* sequence of affine transformations applied to the
* point to be transformed:
*
* translate by (-h/2, -w/2)
* rotate by eu[2] degrees about the origin
* translate by (h/2, w/2)
* translate by (eu[0], eu[1])
*
* The matrix assigned below represents the result of
* combining all of these transformations. Matrix
* elements g and h are always zero in an affine
* transformation.
*/
ale_pos theta = (double) eu[2] * M_PI / 180;
a = (double) cos(theta) * (double) scale_factor;
b = (double) sin(theta) * (double) scale_factor;
c = 0.5 * ((double) input_height * ((double) scale_factor - (double) a)
- (double) input_width * (double) b) + (double) eu[0]
* (double) scale_factor;
d = -b;
e = a;
f = 0.5 * ((double) input_height * (double) b
+ (double) input_width * ((double) scale_factor
- (double) a))
+ (double) eu[1] * (double) scale_factor;
g = 0;
h = 0;
}
resultant_memo = 1;
if (!ale_pos_casting)
ale_pos_disable_casting();
}
/*
* 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;
resultant();
int ale_pos_casting = ale_pos_casting_status();
ale_pos_enable_casting();
/*
* For projective transformations, we calculate
* the inverse of the forward transformation
* matrix.
*/
double scale = (double) a * (double) e - (double) b * (double) d;
_a = ((double) e * 1 - (double) f * (double) h) / scale;
_b = ((double) h * (double) c - 1 * (double) b) / scale;
_c = ((double) b * (double) f - (double) c * (double) e) / scale;
_d = ((double) f * (double) g - (double) d * 1) / scale;
_e = (1 * (double) a - (double) g * (double) c) / scale;
_f = ((double) c * (double) d - (double) a * (double) f) / scale;
_g = ((double) d * (double) h - (double) e * (double) g) / scale;
_h = ((double) g * (double) b - (double) h * (double) a) / scale;
resultant_inverse_memo = 1;
if (!ale_pos_casting)
ale_pos_disable_casting();
}
public:
trans_single &operator=(const trans_single &ta) {
this->trans_abstract::operator=(*((trans_abstract *) &ta));
for (int i = 0; i < 4; i++) {
x[i] = ta.x[i];
}
for (int i = 0; i < 3; i++) {
eu[i] = ta.eu[i];
}
_is_projective = ta._is_projective;
tonal_multiplier = ta.tonal_multiplier;
resultant_memo = 0;
resultant_inverse_memo = 0;
return *this;
}
trans_single(const trans_single &ta) {
operator=(ta);
}
trans_single() {
}
/*
* Returns non-zero if the transformation might be non-Euclidean.
*/
int is_projective() const {
return _is_projective;
}
/*
* Projective/Euclidean transformation
*/
struct point pe(struct point p) const {
struct point result;
resultant();
result[0] = (a * p[0] + b * p[1] + c)
/ (g * p[0] + h * p[1] + 1);
result[1] = (d * p[0] + e * p[1] + f)
/ (g * p[0] + h * p[1] + 1);
return result;
}
/*
* Projective/Euclidean inverse
*/
struct point pei(struct point p) const {
struct point result;
resultant_inverse();
result[0] = (_a * p[0] + _b * p[1] + _c)
/ (_g * p[0] + _h * p[1] + 1);
result[1] = (_d * p[0] + _e * p[1] + _f)
/ (_g * p[0] + _h * p[1] + 1);
return result;
}
#if 0
/*
* operator() is the transformation operator.
*/
struct point operator()(struct point p) {
return transform(p);
}
#endif
/*
* Calculate projective transformation parameters from a euclidean
* transformation.
*/
void eu_to_gpt() {
assert(!_is_projective);
x[0] = transform_unscaled(point( 0 , 0 ) );
x[1] = transform_unscaled(point( input_height, 0 ) );
x[2] = transform_unscaled(point( input_height, input_width ) );
x[3] = transform_unscaled(point( 0 , input_width ) );
resultant_memo = 0;
resultant_inverse_memo = 0;
_is_projective = 1;
}
/*
* Calculate euclidean identity transform for a given image.
*/
static struct trans_single eu_identity(const image *i = NULL, ale_pos scale_factor = 1) {
struct trans_single r;
r.resultant_memo = 0;
r.resultant_inverse_memo = 0;
r.eu[0] = 0;
r.eu[1] = 0;
r.eu[2] = 0;
r.input_width = i ? i->width() : 2;
r.input_height = i ? i->height() : 2;
r.scale_factor = scale_factor;
r._is_projective = 0;
r.tonal_multiplier = pixel(1, 1, 1);
r.bd_set(0, (ale_pos *) NULL);
return r;
}
/*
* Calculate projective identity transform for a given image.
*/
static trans_single gpt_identity(const image *i, ale_pos scale_factor) {
struct trans_single r = eu_identity(i, scale_factor);
r.eu_to_gpt();
return r;
}
/*
* Set the tonal multiplier
*/
void set_tonal_multiplier(pixel p) {
tonal_multiplier = p;
}
pixel get_tonal_multiplier(struct point p) const {
return tonal_multiplier;
}
pixel get_inverse_tonal_multiplier(struct point p) const {
return tonal_multiplier;
}
/*
* Modify a euclidean transform in the indicated manner.
*/
void eu_modify(int i1, ale_pos diff) {
assert(!_is_projective);
resultant_memo = 0;
resultant_inverse_memo = 0;
if (i1 < 2)
eu[i1] += diff / scale_factor;
else
eu[i1] += diff;
}
/*
* Rotate about a given point in the original reference frame.
*/
void eu_rotate_about_scaled(point center, ale_pos diff) {
assert(center.defined());
point fixpoint = scaled_inverse_transform(center);
eu_modify(2, diff);
point offset = center - transform_scaled(fixpoint);
eu_modify(0, offset[0]);
eu_modify(1, offset[1]);
}
/*
* Modify all euclidean parameters at once.
*/
void eu_set(ale_pos eu[3]) {
resultant_memo = 0;
resultant_inverse_memo = 0;
this->eu[0] = eu[0] / scale_factor;
this->eu[1] = eu[1] / scale_factor;
this->eu[2] = eu[2];
if (_is_projective) {
_is_projective = 0;
eu_to_gpt();
}
}
/*
* Get the specified euclidean parameter
*/
ale_pos eu_get(int param) const {
assert (!_is_projective);
assert (param >= 0);
assert (param < 3);
if (param < 2)
return eu[param] * scale_factor;
else
return eu[param];
}
/*
* Modify a projective transform in the indicated manner.
*/
void gpt_modify(int i1, int i2, ale_pos diff) {
assert (_is_projective);
resultant_memo = 0;
resultant_inverse_memo = 0;
x[i2][i1] += diff;
}
/*
* Modify a projective transform according to the group operation.
*/
void gr_modify(int i1, int i2, ale_pos diff) {
assert (_is_projective);
assert (i1 == 0 || i1 == 1);
point diff_vector = (i1 == 0)
? point(diff, 0)
: point(0, diff);
trans_single t = *this;
t.resultant_memo = 0;
t.resultant_inverse_memo = 0;
t.input_height = (unsigned int) scaled_height();
t.input_width = (unsigned int) scaled_width();
t.scale_factor = 1;
t.bd_set(0, (ale_pos *) NULL);
resultant_memo = 0;
resultant_inverse_memo = 0;
x[i2] = t.transform_scaled(t.scaled_inverse_transform(x[i2]) + diff_vector);
}
/*
* Modify all projective parameters at once.
*/
void gpt_set(point x[4]) {
resultant_memo = 0;
resultant_inverse_memo = 0;
_is_projective = 1;
for (int i = 0; i < 4; i++)
this->x[i] = x[i];
}
void gpt_set(point x1, point x2, point x3, point x4) {
point x[4] = {x1, x2, x3, x4};
gpt_set(x);
}
void snap(ale_pos interval) {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 2; j++)
x[i][j] = round(x[i][j] / interval) * interval;
interval /= scale();
for (int i = 0; i < 2; i++)
eu[i] = round(eu[i] / interval) * interval;
interval *= 2 * 180 / M_PI
/ sqrt(pow(unscaled_height(), 2)
+ pow(unscaled_width(), 2));
eu[2] = round(eu[2] / interval) * interval;
resultant_memo = 0;
resultant_inverse_memo = 0;
}
/*
* Get the specified projective parameter
*/
point gpt_get(int point) const {
assert (_is_projective);
assert (point >= 0);
assert (point < 4);
return x[point];
}
/*
* Get the specified projective parameter
*/
ale_pos gpt_get(int point, int dim) {
assert (_is_projective);
assert (dim >= 0);
assert (dim < 2);
return gpt_get(point)[dim];
}
/*
* Translate by a given amount
*/
void translate(point p) {
resultant_memo = 0;
resultant_inverse_memo = 0;
if (_is_projective)
for (int i = 0; i < 4; i++)
x[i] += p;
else {
eu[0] += p[0] / scale_factor;
eu[1] += p[1] / scale_factor;
}
}
/*
* Rotate by a given amount about a given point.
*/
void rotate(point center, ale_pos degrees) {
if (_is_projective)
for (int i = 0; i <= 4; i++) {
ale_pos radians = (double) degrees * M_PI / (double) 180;
x[i] -= center;
x[i] = point(
(double) x[i][0] * cos(radians) + (double) x[i][1] * sin(radians),
(double) x[i][1] * cos(radians) - (double) x[i][0] * sin(radians));
x[i] += center;
resultant_memo = 0;
resultant_inverse_memo = 0;
} else {
assert(center.defined());
point fixpoint = scaled_inverse_transform(center);
eu_modify(2, degrees);
point offset = center - transform_scaled(fixpoint);
eu_modify(0, offset[0]);
eu_modify(1, offset[1]);
}
}
void reset_memos() {
resultant_memo = 0;
resultant_inverse_memo = 0;
}
/*
* Rescale a transform with a given factor.
*/
void specific_rescale(ale_pos factor) {
resultant_memo = 0;
resultant_inverse_memo = 0;
if (_is_projective) {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 2; j++)
x[i][j] *= factor;
} else {
#if 0
/*
* Euclidean scaling is handled in resultant().
*/
for (int i = 0; i < 2; i++)
eu[i] *= factor;
#endif
}
}
/*
* Set the dimensions of the image.
*/
void specific_set_dimensions(const image *im) {
int new_height = (int) im->height();
int new_width = (int) im->width();
if (_is_projective) {
/*
* If P(w, x, y, z) is a projective transform mapping
* the corners of the unit square to points w, x, y, z,
* and Q(w, x, y, z)(i, j) == P(w, x, y, z)(ai, bj),
* then we have:
*
* Q(w, x, y, z) == P( P(w, x, y, z)(0, 0),
* P(w, x, y, z)(a, 0),
* P(w, x, y, z)(a, b),
* P(w, x, y, z)(0, b) )
*
* If we note that P(w, x, y, z)(0, 0) == w, we can
* omit a calculation.
*
* We take 'a' as the ratio (new_height /
* old_height) and 'b' as the ratio (new_width /
* old_width) if we want the common upper left-hand
* region of both new and old images to map to the same
* area.
*
* Since we're not mapping from the unit square, we
* take 'a' as new_height and 'b' as new_width to
* accommodate the existing scale factor.
*/
point _x, _y, _z;
_x = transform_unscaled(point(new_height, 0 ));
_y = transform_unscaled(point(new_height, new_width));
_z = transform_unscaled(point( 0 , new_width));
x[1] = _x;
x[2] = _y;
x[3] = _z;
}
}
/*
* 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.
*/
void gpt_v0_set(point x[4]) {
_is_projective = 1;
/*
* This is slightly modified code from version
* 0.4.0p1.
*/
ale_pos delta_01 = x[1][0] - x[2][0];
ale_pos delta_02 = x[3][0] - x[2][0];
ale_pos sigma_0 = x[0][0] - x[1][0] + x[2][0] - x[3][0];
ale_pos delta_11 = x[1][1] - x[2][1];
ale_pos delta_12 = x[3][1] - x[2][1];
ale_pos sigma_1 = x[0][1] - x[1][1] + x[2][1] - x[3][1];
g = (sigma_0 * delta_12 - sigma_1 * delta_02)
/ (delta_01 * delta_12 - delta_11 * delta_02)
/ (input_width * scale_factor);
h = (delta_01 * sigma_1 - delta_11 * sigma_0 )
/ (delta_01 * delta_12 - delta_11 * delta_02)
/ (input_height * scale_factor);
a = (x[1][0] - x[0][0] + g * x[1][0])
/ (input_width * scale_factor);
b = (x[3][0] - x[0][0] + h * x[3][0])
/ (input_height * scale_factor);
c = x[0][0];
d = (x[1][1] - x[0][1] + g * x[1][1])
/ (input_width * scale_factor);
e = (x[3][1] - x[0][1] + h * x[3][1])
/ (input_height * scale_factor);
f = x[0][1];
resultant_memo = 1;
resultant_inverse_memo = 0;
this->x[0] = scaled_inverse_transform( point( 0 , 0 ) );
this->x[1] = scaled_inverse_transform( point( (input_height * scale_factor), 0 ) );
this->x[2] = scaled_inverse_transform( point( (input_height * scale_factor), (input_width * scale_factor) ) );
this->x[3] = scaled_inverse_transform( point( 0 , (input_width * scale_factor) ) );
resultant_memo = 0;
resultant_inverse_memo = 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.
*/
void eu_v0_set(ale_pos eu[3]) {
/*
* This is slightly modified code from version
* 0.4.0p1.
*/
int i;
x[0][0] = 0; x[0][1] = 0;
x[1][0] = (input_width * scale_factor); x[1][1] = 0;
x[2][0] = (input_width * scale_factor); x[2][1] = (input_height * scale_factor);
x[3][0] = 0; x[3][1] = (input_height * scale_factor);
/*
* Rotate
*/
ale_pos theta = (double) eu[2] * M_PI / 180;
for (i = 0; i < 4; i++) {
ale_pos _x[2];
_x[0] = ((double) x[i][0] - (double) (input_width * scale_factor)/2) * cos(theta)
+ ((double) x[i][1] - (double) (input_height * scale_factor)/2) * sin(theta)
+ (input_width * scale_factor)/2;
_x[1] = ((double) x[i][1] - (double) (input_height * scale_factor)/2) * cos(theta)
- ((double) x[i][0] - (double) (input_width * scale_factor)/2) * sin(theta)
+ (input_height * scale_factor)/2;
x[i][0] = _x[0];
x[i][1] = _x[1];
}
/*
* Translate
*/
for (i = 0; i < 4; i++) {
x[i][0] += eu[0];
x[i][1] += eu[1];
}
if (_is_projective) {
gpt_v0_set(x);
return;
}
/*
* Reconstruct euclidean parameters
*/
gpt_v0_set(x);
point center((input_height * scale_factor) / 2, (input_width * scale_factor) / 2);
point center_image = transform_scaled(center);
this->eu[0] = (center_image[0] - center[0]) / scale_factor;
this->eu[1] = (center_image[1] - center[1]) / scale_factor;
point center_left((input_height * scale_factor) / 2, 0);
point center_left_image = transform_scaled(center_left);
ale_pos displacement = center_image[0] - center_left_image[0];
this->eu[2] = asin(2 * displacement / (input_width * scale_factor)) / M_PI * 180;
if (center_left_image[1] > center_image[1])
this->eu[2] = this->eu[2] + 180;
resultant_memo = 0;
resultant_inverse_memo = 0;
_is_projective = 0;
}
void debug_output() {
fprintf(stderr, "[t.do ih=%u, iw=%d x=[[%f %f] [%f %f] [%f %f] [%f %f]] eu=[%f %f %f]\n"
" a-f=[%f %f %f %f %f %f %f %f] _a-_f=[%f %f %f %f %f %f %f %f]\n"
" bdcnm=%d ip=%d rm=%d rim=%d sf=%f]\n",
input_height, input_width,
(double) x[0][0], (double) x[0][1],
(double) x[1][0], (double) x[1][1],
(double) x[2][0], (double) x[2][1],
(double) x[3][0], (double) x[3][1],
(double) eu[0], (double) eu[1], (double) eu[2],
(double) a, (double) b, (double) c, (double) d,
(double) e, (double) f, (double) g, (double) h,
(double) _a, (double) _b, (double) _c, (double) _d,
(double) _e, (double) _f, (double) _g, (double) _h,
bd_count(),
_is_projective,
resultant_memo,
resultant_inverse_memo,
(double) scale_factor);
}
};
#endif

38
d2/transformation.h Normal file
View File

@@ -0,0 +1,38 @@
// 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
*/
/*
* transformation.h: Represent transformations of the kind q = c(b^-1(p)),
* where p is a point in the source coordinate system, q is a point in the
* target coordinate system, b^-1 is a transformation correcting barrel
* distortion, and c is a transformation of projective or Euclidean type.
* (Note that ^-1 in this context indicates the function inverse rather than
* the exponential.)
*/
#ifndef __transformation_h__
#define __transformation_h__
#include "trans_single.h"
#include "trans_multi.h"
typedef trans_multi transformation;
#endif

90
d2/vise.h Normal file
View File

@@ -0,0 +1,90 @@
// 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
*/
/*
* vise.h: A superclass for all video stabilization engine classes.
*/
#ifndef __vise_h__
#define __vise_h__
#include "transformation.h"
#include "image.h"
#include "point.h"
/*
* Class vise accepts images from vise_core. It is assumed, when an image is
* received, that all transformations required for stabilization are available.
* This class is abstract, and must be subclassed to be instantiated.
*/
class vise {
const char *prefix;
const char *suffix;
protected:
ale_real scale_factor;
render *r;
/*
* Write a frame. This function does not support sequences having
* more than 100,000,000 frames (starting count with frame zero).
*/
void write_frame(const image *im, unsigned int frame_number) {
if (frame_number > 99999999) {
fprintf(stderr, "\n\n *** Frame count too high for d2::vise::write_frame() ***\n\n\n");
exit(1);
}
int length = strlen(prefix) + strlen(suffix) + 8 + 1;
char *filename_string = (char *) malloc(length * sizeof(char));
snprintf(filename_string, length, "%s%08d%s", prefix, frame_number, suffix);
image_rw::write_image(filename_string, im, &image_rw::exp());
free(filename_string);
}
public:
vise(render *r, const char *prefix, const char *suffix, ale_real scale_factor) {
this->prefix = prefix;
this->suffix = suffix;
this->r = r;
this->scale_factor = scale_factor;
}
/*
* Accept an image for rendering.
*/
virtual void render_frame(unsigned int frame_number) = 0;
/*
* Report the frame lag for this stabilizer.
*/
virtual unsigned int lag() = 0;
virtual ~vise() {
}
};
#endif

210
d2/vise/ma.h Normal file
View File

@@ -0,0 +1,210 @@
// 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
*/
/*
* ma.h: A video stabilizer that uses moving averages to calculate
* transformations.
*/
#ifndef __ma_h__
#define __ma_h__
#include "../vise.h"
#include "../image.h"
#include "../point.h"
/*
* Stabilize using moving averages.
*
* For a given frame x, the moving averages are calculated over frames
* ranging from x - r to x + r, where r is the specified RANGE size.
*/
class ma : public vise {
unsigned int range;
public:
ma(render *r, unsigned int range, const char *prefix, const char *suffix,
ale_real scale_factor) : vise(r, prefix, suffix, scale_factor) {
r->extend_queue(range);
this->range = range;
}
/*
* Accept an image for rendering.
*/
void render_frame(unsigned int frame_number) {
const image *im = r->get_image(frame_number);
int replace = 0;
int replace_ex = 0;
const filter::scaled_filter *scf = NULL;
unsigned int rx_count = render::get_rx_count();
const exclusion *rx_parameters = render::get_rx_parameters();
int rx_show = render::is_rx_show();
/*
* Determine, for single-invariant chains, whether replacement
* is occurring, and, if so, determine whether we are honoring
* exclusion regions.
*/
if (typeid(*r) == typeid(incremental)
&& ((incremental *)r)->get_invariant()->is_last()) {
scf = ((incremental *)r)->get_invariant()->ssfe()->get_scaled_filter();
if (scf->is_coarse() && scf->get_filter()->support() >= 1) {
replace = 1;
replace_ex = ((incremental *)r)->get_invariant()->ssfe()->ex_is_honored();
}
}
/*
* Calculate the parameters for the desired
* transformation.
*/
point p[4] = {
point(0, 0),
point(0, 0),
point(0, 0),
point(0, 0)
};
ale_pos bd[BARREL_DEGREE] = {0, /* ... */};
unsigned int bd_count = BARREL_DEGREE;
int frame_count = 0;
for (int f = frame_number - range; f <= (int) (frame_number + range); f++) {
if (f < 0
|| f >= (int) image_rw::count())
continue;
frame_count++;
transformation t = align::of(f);
for (unsigned int i = 0; i < 4; i++)
p[i] = p[i] + t.transform_scaled(point((i == 1 || i == 2) ? t.scaled_height() : ale_pos_0,
(i > 1) ? t.scaled_width() : ale_pos_0));
if (t.bd_count() < bd_count)
bd_count = t.bd_count();
for (unsigned int i = 0; i < bd_count; i++)
bd[i] += t.bd_get(i);
}
for (unsigned int i = 0; i < 4; i++)
p[i] = p[i] / (ale_pos) frame_count;
for (unsigned int i = 0; i < bd_count; i++)
bd[i] /= frame_count;
/*
* Generate the desired transformation.
*/
transformation t = align::of(frame_number);
transformation s = t;
unsigned int new_height = (unsigned int)
floor(s.unscaled_height() * scale_factor);
unsigned int new_width = (unsigned int)
floor(s.unscaled_width() * scale_factor);
s.set_domain(new_height, new_width);
s.gpt_set(p);
s.bd_set(bd_count, bd);
image *rendered = new_image_ale_real(new_height, new_width, 3);
if (replace) {
const image *replace_image = NULL;
replace_image = image_rw::open(frame_number);
scf->set_parameters(t, s, replace_image);
}
for (unsigned int i = 0; i < rendered->height(); i++)
for (unsigned int j = 0; j < rendered->width(); j++) {
point unoffset_p = s.transform_scaled(point(i, j));
point p = unoffset_p - im->offset();
// point p_replace = t.inverse_transform(s(point(i, j)));
double shading = 1;
if (rx_show || (replace && replace_ex))
for (unsigned int param = 0; param < rx_count; param++) {
if (rx_parameters[param].type == exclusion::RENDER
&& unoffset_p[0] >= rx_parameters[param].x[0]
&& unoffset_p[0] <= rx_parameters[param].x[1]
&& unoffset_p[1] >= rx_parameters[param].x[2]
&& unoffset_p[1] <= rx_parameters[param].x[3]
&& frame_number >= (unsigned) rx_parameters[param].x[4]
&& frame_number <= (unsigned) rx_parameters[param].x[5])
shading *= 0.5;
if (rx_parameters[param].type == exclusion::FRAME
&& i >= rx_parameters[param].x[0] * (ale_pos) scale_factor
&& i <= rx_parameters[param].x[1] * (ale_pos) scale_factor
&& j >= rx_parameters[param].x[2] * (ale_pos) scale_factor
&& j <= rx_parameters[param].x[3] * (ale_pos) scale_factor
&& frame_number >= (unsigned) rx_parameters[param].x[4]
&& frame_number <= (unsigned) rx_parameters[param].x[5])
shading *= 0.5;
}
if (shading < 1 && !rx_show && replace && replace_ex)
continue;
if (replace) {
pixel value, weight;
scf->filtered(i, j, &value, &weight, replace_ex, frame_number);
if (weight.min_norm() > ale_real_weight_floor) {
rendered->set_pixel(i, j, shading * (value / weight));
continue;
}
}
if (p[0] >= 0
&& p[0] <= im->height() - 1
&& p[1] >= 0
&& p[1] <= im->width() - 1)
rendered->set_pixel(i, j, shading * im->get_bl(p));
else
rendered->set_pixel(i, j, pixel(0, 0, 0));
}
if (replace)
image_rw::close(frame_number);
write_frame(rendered, frame_number);
delete rendered;
}
/*
* Report the frame lag for this stabilizer.
*/
unsigned int lag() {
return range;
}
};
#endif

164
d2/vise/sf.h Normal file
View File

@@ -0,0 +1,164 @@
// 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
*/
/*
* sf.h: A video stabilizer that uses a single frame's transformation.
*/
#ifndef __sf_h__
#define __sf_h__
#include "../vise.h"
#include "../image.h"
#include "../point.h"
/*
* Stabilize to the viewpoint of a single frame.
*/
class sf : public vise {
unsigned int frame;
public:
sf(render *r, unsigned int frame, const char *prefix, const char *suffix,
ale_real scale_factor) : vise(r, prefix, suffix, scale_factor) {
if (frame > 10) {
fprintf(stderr, "\n\n*** Warning: large values <x> for VISE sf:<x> are not recommended ***\n\n");
}
r->extend_queue(frame);
this->frame = frame;
}
/*
* Accept an image for rendering.
*/
void render_frame(unsigned int frame_number) {
const image *im = r->get_image(frame_number);
int replace = 0; // Are image regions being replaced?
int replace_ex = 0; // If image regions are being replaced, are we honoring exclusion regions?
unsigned int rx_count = render::get_rx_count();
const filter::scaled_filter *scf = NULL;
const exclusion *rx_parameters = render::get_rx_parameters();
int rx_show = render::is_rx_show();
/*
* Determine, for single-invariant chains, whether replacement
* is occurring, and, if so, determine whether we are honoring
* exclusion regions.
*/
if (typeid(*r) == typeid(incremental)
&& ((incremental *)r)->get_invariant()->is_last()) {
scf = ((incremental *)r)->get_invariant()->ssfe()->get_scaled_filter();
assert(scf);
if (scf->is_coarse() && scf->get_filter()->support() >= 1) {
replace = 1;
replace_ex = ((incremental *)r)->get_invariant()->ssfe()->ex_is_honored();
}
}
/*
* Generate the desired transformation.
*/
transformation t = align::of(frame_number);
transformation s = align::of(frame);
unsigned int new_height = (unsigned int)
floor(s.unscaled_height() * scale_factor);
unsigned int new_width = (unsigned int)
floor(s.unscaled_width() * scale_factor);
s.set_domain(new_height, new_width);
image *rendered = new_image_ale_real(new_height, new_width, 3);
const image *replace_image = NULL;
if (replace) {
replace_image = image_rw::open(frame_number);
scf->set_parameters(t, s, replace_image);
}
for (unsigned int i = 0; i < rendered->height(); i++)
for (unsigned int j = 0; j < rendered->width(); j++) {
point unoffset_p = s.transform_scaled(point(i, j));
point p = unoffset_p - im->offset();
double shading = 1;
if (rx_show || (replace && replace_ex))
for (unsigned int param = 0; param < rx_count; param++) {
if (rx_parameters[param].type == exclusion::RENDER
&& unoffset_p[0] >= rx_parameters[param].x[0]
&& unoffset_p[0] <= rx_parameters[param].x[1]
&& unoffset_p[1] >= rx_parameters[param].x[2]
&& unoffset_p[1] <= rx_parameters[param].x[3]
&& frame_number >= (unsigned) rx_parameters[param].x[4]
&& frame_number <= (unsigned) rx_parameters[param].x[5])
shading *= 0.5;
if (rx_parameters[param].type == exclusion::FRAME
&& i >= rx_parameters[param].x[0] * (ale_pos) scale_factor
&& i <= rx_parameters[param].x[1] * (ale_pos) scale_factor
&& j >= rx_parameters[param].x[2] * (ale_pos) scale_factor
&& j <= rx_parameters[param].x[3] * (ale_pos) scale_factor
&& frame_number >= (unsigned) rx_parameters[param].x[4]
&& frame_number <= (unsigned) rx_parameters[param].x[5])
shading *= 0.5;
}
if (shading < 1 && !rx_show && replace && replace_ex)
continue;
if (replace) {
pixel value, weight;
scf->filtered(i, j, &value, &weight, replace_ex, frame_number);
if (weight.min_norm() > ale_real_weight_floor) {
rendered->set_pixel(i, j, shading * (value / weight));
continue;
}
}
if (p[0] >= 0
&& p[0] <= im->height() - 1
&& p[1] >= 0
&& p[1] <= im->width() - 1)
rendered->set_pixel(i, j, shading * im->get_bl(p));
else
rendered->set_pixel(i, j, pixel(0, 0, 0));
}
if (replace)
image_rw::close(frame_number);
write_frame(rendered, frame_number);
delete rendered;
}
/*
* Report the frame lag for this stabilizer.
*/
unsigned int lag() {
return frame;
}
};
#endif

29
d2/vise_core.cc Normal file
View File

@@ -0,0 +1,29 @@
// 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
*/
#include "vise_core.h"
/*
* See vise_core.h for details on these variables.
*/
vise **vise_core::active = NULL;
unsigned int vise_core::active_size = 0;
ale_real vise_core::scale_factor = 1;

146
d2/vise_core.h Normal file
View File

@@ -0,0 +1,146 @@
// 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
*/
/*
* vise_core.h: Manages instances of vise.
*/
#ifndef __vise_core_h__
#define __vise_core_h__
#include "vise.h"
#include "image.h"
#include "vise/ma.h"
#include "vise/sf.h"
/*
* Vise_core initializes, and maintains shared variables for, all instances of
* vise.
*/
class vise_core {
static vise **active;
static unsigned int active_size;
static ale_real scale_factor;
public:
/*
* Set the VISE scale factor.
*/
static void set_scale(ale_real factor) {
scale_factor = factor;
}
/*
* Add a new video stabilization engine.
*/
static void add(render *chain, const char *type, const char *prefix, const char *suffix) {
/*
* Instantiate an engine of the appropriate type.
*/
if (!strcmp(type, "identity")) {
/*
* Identity is a moving average 0 frames to either side.
*/
active = (vise **) realloc(active, ++active_size * sizeof(vise *));
assert(active);
if (active == NULL) {
fprintf(stderr, "\n\n*** VISE: Unable to allocate memory ***\n\n\n");
exit(1);
}
active[active_size - 1] = new ma(chain, 0, prefix, suffix, scale_factor);
} else if (!strncmp(type, "ma:", 3)) {
/*
* Moving average with an unsigned range parameter.
*/
unsigned int range;
if(sscanf(type + 3, "%u", &range) != 1) {
fprintf(stderr, "\n\n*** VISE: 'ma:' type requires an unsigned argument. ***\n\n\n");
exit(1);
}
active = (vise **) realloc(active, ++active_size * sizeof(vise *));
assert(active);
if (active == NULL) {
fprintf(stderr, "\n\n*** VISE: Unable to allocate memory ***\n\n\n");
exit(1);
}
active[active_size - 1] = new ma(chain, range, prefix, suffix, scale_factor);
} else if (!strncmp(type, "sf:", 3)) {
/*
* Single frame with an unsigned frame parameter.
*/
unsigned int frame;
if(sscanf(type + 3, "%u", &frame) != 1) {
fprintf(stderr, "\n\n*** VISE: 'sf:' type requires an unsigned argument. ***\n\n\n");
exit(1);
}
active = (vise **) realloc(active, ++active_size * sizeof(vise *));
assert(active);
if (active == NULL) {
fprintf(stderr, "\n\n*** VISE: Unable to allocate memory ***\n\n\n");
exit(1);
}
active[active_size - 1] = new sf(chain, frame, prefix, suffix, scale_factor);
} else {
fprintf(stderr, "\n\n*** VISE: Unknown type '%s' ***\n\n\n", type);
exit(1);
}
}
/*
* Add a new image to the rendering queue.
*/
static void frame_queue_add(unsigned int frame_number) {
/*
* Process the current queue.
*/
for (unsigned int i = 0; i < active_size; i++) {
int lag = active[i]->lag();
if ((int) frame_number - lag >= 0)
active[i]->render_frame(frame_number - lag);
}
/*
* If this is the last frame, then complete all rendering.
*/
if (frame_number == image_rw::count() - 1)
for (unsigned int i = 0; i < active_size; i++)
for (int j = active[i]->lag() - 1; j >= 0; j--)
active[i]->render_frame(frame_number - j);
}
};
#endif