428 lines
10 KiB
C++
428 lines
10 KiB
C++
// 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
|