1002 lines
24 KiB
C++
1002 lines
24 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
|
|
*/
|
|
|
|
/*
|
|
* trans_multi.h: Represent multiple transformations, affecting different
|
|
* regions of a scene.
|
|
*/
|
|
|
|
#ifndef __trans_multi_h__
|
|
#define __trans_multi_h__
|
|
|
|
#include "trans_abstract.h"
|
|
#include "trans_single.h"
|
|
|
|
struct trans_multi : public trans_abstract {
|
|
public:
|
|
struct multi_coordinate {
|
|
int degree;
|
|
int x;
|
|
int y;
|
|
|
|
public:
|
|
int operator<(const multi_coordinate &mc) const {
|
|
if (degree < mc.degree
|
|
|| degree == mc.degree && y < mc.y
|
|
|| degree == mc.degree && y == mc.y && x < mc.x)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
private:
|
|
static unsigned int _multi;
|
|
static ale_pos _multi_decomp;
|
|
static ale_real _multi_improvement;
|
|
|
|
typedef unsigned short index_t;
|
|
|
|
std::vector<trans_single> trans_stack;
|
|
std::vector<multi_coordinate> coord_stack;
|
|
std::map<multi_coordinate, index_t> coordinate_map;
|
|
|
|
int use_multi;
|
|
index_t current_element;
|
|
|
|
index_t orig_ref_height, orig_ref_width;
|
|
index_t cur_ref_height, cur_ref_width;
|
|
point cur_offset;
|
|
point orig_offset;
|
|
|
|
index_t *spatio_elem_map;
|
|
index_t *spatio_elem_map_r;
|
|
|
|
void push_element() {
|
|
assert (trans_stack.size() > 0);
|
|
|
|
if (++current_element == trans_stack.size())
|
|
trans_stack.push_back(trans_stack.back());
|
|
}
|
|
|
|
trans_multi() : trans_stack() {
|
|
use_multi = 0;
|
|
current_element = 0;
|
|
orig_ref_height = 0;
|
|
orig_ref_width = 0;
|
|
cur_ref_height = 0;
|
|
cur_ref_width = 0;
|
|
spatio_elem_map = NULL;
|
|
spatio_elem_map_r = NULL;
|
|
}
|
|
|
|
public:
|
|
|
|
static void set_md(double d) {
|
|
if (!(d > 1))
|
|
d = 1;
|
|
|
|
_multi_decomp = d;
|
|
}
|
|
|
|
static void set_multi(const char *type) {
|
|
if (!strcmp(type, "none")) {
|
|
_multi = 0;
|
|
} else if (!strcmp(type, "local")) {
|
|
_multi = 1;
|
|
} else if (!strcmp(type, "fill")) {
|
|
_multi = 2;
|
|
} else if (!strcmp(type, "llocal")) {
|
|
_multi = 3;
|
|
} else if (!strcmp(type, "global")) {
|
|
_multi = 4;
|
|
}
|
|
}
|
|
|
|
static void set_mi(double d) {
|
|
_multi_improvement = d;
|
|
}
|
|
|
|
/*
|
|
* Calculate euclidean identity transform for a given image.
|
|
*/
|
|
static struct trans_multi eu_identity(const image *i = NULL, ale_pos scale_factor = 1) {
|
|
struct trans_multi r;
|
|
multi_coordinate mc;
|
|
|
|
mc.degree = 0;
|
|
mc.x = 0;
|
|
mc.y = 0;
|
|
|
|
r.input_width = i ? i->width() : 2;
|
|
r.input_height = i ? i->height() : 2;
|
|
r.scale_factor = scale_factor;
|
|
r.trans_stack.push_back(trans_single::eu_identity(i, scale_factor));
|
|
r.coord_stack.push_back(mc);
|
|
r.coordinate_map[mc] = r.trans_stack.size() - 1;
|
|
r.current_element = 0;
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Generate an array of identity transformations.
|
|
*/
|
|
static trans_multi *new_eu_identity_array(unsigned int size) {
|
|
trans_multi *result = new trans_multi[size];
|
|
for (unsigned int i = 0; i < size; i++)
|
|
result[i] = eu_identity();
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Calculate projective transformation parameters from a euclidean
|
|
* transformation.
|
|
*/
|
|
void eu_to_gpt() {
|
|
for (unsigned int t = 0; t < trans_stack.size(); t++)
|
|
trans_stack[t].eu_to_gpt();
|
|
}
|
|
|
|
/*
|
|
* Calculate projective identity transform for a given image.
|
|
*/
|
|
static trans_multi gpt_identity(const image *i, ale_pos scale_factor) {
|
|
struct trans_multi r = eu_identity(i, scale_factor);
|
|
r.eu_to_gpt();
|
|
return r;
|
|
}
|
|
|
|
trans_multi &operator=(const trans_multi &tm) {
|
|
this->trans_abstract::operator=(*((trans_abstract *) &tm));
|
|
|
|
trans_stack = tm.trans_stack;
|
|
coord_stack = tm.coord_stack;
|
|
coordinate_map = tm.coordinate_map;
|
|
|
|
use_multi = tm.use_multi;
|
|
current_element = tm.current_element;
|
|
|
|
orig_ref_height = tm.orig_ref_height;
|
|
orig_ref_width = tm.orig_ref_width;
|
|
cur_ref_height = tm.cur_ref_height;
|
|
cur_ref_width = tm.cur_ref_width;
|
|
cur_offset = tm.cur_offset;
|
|
orig_offset = tm.orig_offset;
|
|
|
|
free(spatio_elem_map);
|
|
free(spatio_elem_map_r);
|
|
spatio_elem_map = NULL;
|
|
spatio_elem_map_r = NULL;
|
|
|
|
size_t cur_size = cur_ref_width * cur_ref_height * sizeof(index_t);
|
|
|
|
if (cur_size > 0 && tm.spatio_elem_map) {
|
|
spatio_elem_map = (index_t *) malloc(cur_size);
|
|
assert (spatio_elem_map);
|
|
memcpy(spatio_elem_map, tm.spatio_elem_map, cur_size);
|
|
}
|
|
|
|
cur_size = input_height * input_width * sizeof(index_t);
|
|
if (cur_size > 0 && tm.spatio_elem_map_r) {
|
|
spatio_elem_map_r = (index_t *) malloc(cur_size);
|
|
assert (spatio_elem_map_r);
|
|
memcpy(spatio_elem_map_r, tm.spatio_elem_map_r, cur_size);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
trans_multi(const trans_multi &tm) : trans_stack() {
|
|
spatio_elem_map = NULL;
|
|
spatio_elem_map_r = NULL;
|
|
operator=(tm);
|
|
}
|
|
|
|
~trans_multi() {
|
|
free(spatio_elem_map);
|
|
free(spatio_elem_map_r);
|
|
}
|
|
|
|
trans_single get_element(index_t index) const {
|
|
assert (index < trans_stack.size());
|
|
|
|
return trans_stack[index];
|
|
}
|
|
|
|
trans_single get_element(multi_coordinate m) {
|
|
assert(coordinate_map.count(m));
|
|
index_t index = coordinate_map[m];
|
|
|
|
return get_element(index);
|
|
}
|
|
|
|
index_t get_index(multi_coordinate m) {
|
|
assert(coordinate_map.count(m));
|
|
return coordinate_map[m];
|
|
}
|
|
|
|
int exists(multi_coordinate m) {
|
|
return coordinate_map.count(m);
|
|
}
|
|
|
|
trans_single get_current_element() const {
|
|
return get_element(current_element);
|
|
}
|
|
|
|
void set_element(index_t index, trans_single t) {
|
|
assert (index < trans_stack.size());
|
|
|
|
trans_stack[index] = t;
|
|
}
|
|
|
|
void set_current_element(trans_single t) {
|
|
set_element(current_element, t);
|
|
}
|
|
|
|
void set_current_element(const trans_multi &t) {
|
|
set_element(current_element, t.get_current_element());
|
|
}
|
|
|
|
index_t get_current_index() const {
|
|
return current_element;
|
|
}
|
|
|
|
multi_coordinate get_current_coordinate() const {
|
|
return coord_stack[current_element];
|
|
}
|
|
|
|
multi_coordinate get_coordinate(index_t i) const {
|
|
assert(i < trans_stack.size());
|
|
return coord_stack[i];
|
|
}
|
|
|
|
void set_current_index(index_t i) {
|
|
assert (i < trans_stack.size());
|
|
current_element = i;
|
|
}
|
|
|
|
/*
|
|
* Set the bounds of the reference image after incorporation
|
|
* of the original frame.
|
|
*/
|
|
void set_original_bounds(const image *i) {
|
|
assert (orig_ref_width == 0);
|
|
assert (orig_ref_height == 0);
|
|
|
|
orig_ref_height = i->height();
|
|
orig_ref_width = i->width();
|
|
orig_offset = i->offset();
|
|
|
|
assert (orig_ref_width != 0);
|
|
assert (orig_ref_height != 0);
|
|
}
|
|
|
|
static multi_coordinate parent_mc(multi_coordinate mc) {
|
|
multi_coordinate result;
|
|
|
|
assert (mc.degree > 0);
|
|
|
|
if (mc.degree == 1) {
|
|
result.degree = 0;
|
|
result.x = 0;
|
|
result.y = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
result.degree = mc.degree - 1;
|
|
result.x = (int) floor((double) mc.x / (double) 2);
|
|
result.y = (int) floor((double) mc.y / (double) 2);
|
|
|
|
return result;
|
|
}
|
|
|
|
index_t parent_index(index_t i) {
|
|
multi_coordinate mc = coord_stack[i];
|
|
multi_coordinate mcp = parent_mc(mc);
|
|
index_t result = coordinate_map[mcp];
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Set the bounds of the reference image after incorporation
|
|
* of the most recent frame.
|
|
*/
|
|
void set_current_bounds(const image *i) {
|
|
use_multi = 0;
|
|
free(spatio_elem_map);
|
|
free(spatio_elem_map_r);
|
|
spatio_elem_map = NULL;
|
|
spatio_elem_map_r = NULL;
|
|
|
|
cur_ref_height = i->height();
|
|
cur_ref_width = i->width();
|
|
cur_offset = i->offset();
|
|
|
|
int d; ale_pos div;
|
|
for (d = 1, div = 2;
|
|
orig_ref_height / div >= _multi_decomp
|
|
&& orig_ref_width / div >= _multi_decomp
|
|
&& _multi > 0;
|
|
d++, div *= 2) {
|
|
|
|
ale_pos height_scale = orig_ref_height / div;
|
|
ale_pos width_scale = orig_ref_width / div;
|
|
|
|
for (int i = floor((cur_offset[0] - orig_offset[0]) / height_scale);
|
|
i < ceil((cur_offset[0] - orig_offset[0] + cur_ref_height) / height_scale);
|
|
i++)
|
|
for (int j = floor((cur_offset[1] - orig_offset[1]) / width_scale);
|
|
j < ceil((cur_offset[1] - orig_offset[1] + cur_ref_width) / width_scale);
|
|
j++) {
|
|
|
|
multi_coordinate c;
|
|
c.degree = d;
|
|
c.x = j;
|
|
c.y = i;
|
|
|
|
if (!coordinate_map.count(c)) {
|
|
multi_coordinate parent = parent_mc(c);
|
|
assert (coordinate_map.count(parent));
|
|
trans_stack.push_back(trans_stack[coordinate_map[parent]]);
|
|
coord_stack.push_back(c);
|
|
coordinate_map[c] = trans_stack.size() - 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
index_t stack_depth() const {
|
|
return trans_stack.size();
|
|
}
|
|
|
|
struct elem_bounds_int_t {
|
|
unsigned int imin, imax, jmin, jmax;
|
|
|
|
int satisfies_min_dim(unsigned int min_dimension) {
|
|
if (imax - imin < min_dimension
|
|
|| jmax - jmin < min_dimension)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
struct elem_bounds_t {
|
|
ale_pos imin, imax, jmin, jmax;
|
|
|
|
elem_bounds_int_t scale_to_bounds(unsigned int height, unsigned int width) {
|
|
elem_bounds_t e;
|
|
elem_bounds_int_t f;
|
|
|
|
e = *this;
|
|
|
|
e.imin *= height;
|
|
e.imax *= height;
|
|
e.jmin *= width;
|
|
e.jmax *= width;
|
|
|
|
if (e.imin > 0)
|
|
f.imin = (unsigned int) floor(e.imin);
|
|
else
|
|
f.imin = 0;
|
|
if (e.imax < height)
|
|
f.imax = (unsigned int) ceil(e.imax);
|
|
else
|
|
f.imax = height;
|
|
if (e.jmin > 0)
|
|
f.jmin = (unsigned int) floor(e.jmin);
|
|
else
|
|
f.jmin = 0;
|
|
if (e.jmax < width)
|
|
f.jmax = (unsigned int) ceil(e.jmax);
|
|
else
|
|
f.jmax = width;
|
|
|
|
return f;
|
|
}
|
|
};
|
|
|
|
elem_bounds_t elem_bounds(int e) const {
|
|
elem_bounds_t result;
|
|
|
|
result.imin = cur_offset[0] - orig_offset[0];
|
|
result.imax = result.imin + cur_ref_height;
|
|
result.jmin = cur_offset[1] - orig_offset[1];
|
|
result.jmax = result.jmin + cur_ref_width;
|
|
|
|
if (e > 0) {
|
|
multi_coordinate mc = coord_stack[e];
|
|
|
|
ale_pos height_scale = orig_ref_height / pow(2, mc.degree);
|
|
ale_pos width_scale = orig_ref_width / pow(2, mc.degree);
|
|
|
|
if (height_scale * mc.y > result.imin)
|
|
result.imin = height_scale * mc.y;
|
|
if (height_scale * (mc.y + 1) < result.imax)
|
|
result.imax = height_scale * (mc.y + 1);
|
|
if (width_scale * mc.x > result.jmin)
|
|
result.jmin = width_scale * mc.x;
|
|
if (width_scale * (mc.x + 1) < result.jmax)
|
|
result.jmax = width_scale * (mc.x + 1);
|
|
}
|
|
|
|
result.imin -= cur_offset[0] - orig_offset[0];
|
|
result.imax -= cur_offset[0] - orig_offset[0];
|
|
result.jmin -= cur_offset[1] - orig_offset[1];
|
|
result.jmax -= cur_offset[1] - orig_offset[1];
|
|
|
|
|
|
result.imin /= cur_ref_height;
|
|
result.imax /= cur_ref_height;
|
|
result.jmin /= cur_ref_width;
|
|
result.jmax /= cur_ref_width;
|
|
|
|
return result;
|
|
}
|
|
|
|
elem_bounds_t elem_bounds() const {
|
|
return elem_bounds(current_element);
|
|
}
|
|
private:
|
|
int check_multi(int i, int j, pixel value, const image *cur_ref, const image *input, index_t check_index) {
|
|
|
|
int result = 0;
|
|
const pixel &rp = value;
|
|
index_t index = check_index;
|
|
|
|
trans_single t = get_element(index);
|
|
point p0 = point(cur_offset[0] + i, cur_offset[1] + j);
|
|
point p = t.unscaled_inverse_transform(p0);
|
|
|
|
if (!input->in_bounds(p))
|
|
return result;
|
|
|
|
trans_single s = get_element(spatio_elem_map[cur_ref_width * i + j]);
|
|
|
|
point q = s.unscaled_inverse_transform(p0);
|
|
|
|
pixel pt = t.get_tonal_multiplier(p0);
|
|
pixel qt = s.get_tonal_multiplier(p0);
|
|
|
|
if (input->in_bounds(q)) {
|
|
pixel ip1 = input->get_bl(p);
|
|
pixel ip0 = input->get_bl(q);
|
|
|
|
ale_real diff1 = (pt * ip1 - rp).norm();
|
|
ale_real diff0 = (qt * ip0 - rp).norm();
|
|
|
|
/*
|
|
* 0.99 factor is for cycle avoidance (e.g., in
|
|
* filling).
|
|
*/
|
|
|
|
if (diff1 < diff0 * 0.99 /* * (1 - _multi_improvement) */
|
|
|| _multi == 3) {
|
|
result = 1;
|
|
spatio_elem_map[cur_ref_width * i + j] = index;
|
|
}
|
|
}
|
|
|
|
int ii = (int) p[0];
|
|
int jj = (int) p[1];
|
|
|
|
if (ii < 0 || (unsigned int) ii >= input_height
|
|
|| jj < 0 || (unsigned int) jj >= input_width)
|
|
return result;
|
|
|
|
trans_single u = get_element(spatio_elem_map_r[input_width * ii + jj]);
|
|
point r = u.transform_unscaled(p);
|
|
|
|
pixel ut = u.get_tonal_multiplier(r);
|
|
|
|
if (cur_ref->in_bounds(r - cur_offset)) {
|
|
pixel ip1 = input->get_bl(p);
|
|
pixel rp0 = cur_ref->get_bl(r - cur_offset);
|
|
|
|
ale_real diff1 = (pt * ip1 - rp).norm();
|
|
ale_real diff0 = (ut * ip1 - rp0).norm();
|
|
|
|
/*
|
|
* 0.99 factor is probably not necessary, but
|
|
* is included for symmetry with cycle-avoidance
|
|
* factor.
|
|
*/
|
|
|
|
if (diff1 < diff0 * 0.99 /* * (1 - _multi_improvement) */
|
|
|| _multi == 3) {
|
|
spatio_elem_map_r[input_width * ii + jj] = index;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void assign_multi_global_best(const image *cur_ref, const image *input) {
|
|
for (unsigned int i = 0; i < cur_ref_height; i++)
|
|
for (unsigned int j = 0; j < cur_ref_width; j++) {
|
|
pixel rp = cur_ref->get_pixel(i, j);
|
|
for (index_t index = 0; index < coordinate_map.size(); index++)
|
|
check_multi(i, j, rp, cur_ref, input, index);
|
|
}
|
|
}
|
|
|
|
void assign_multi_best(const image *cur_ref, const image *input) {
|
|
for (unsigned int i = 0; i < cur_ref_height; i++)
|
|
for (unsigned int j = 0; j < cur_ref_width; j++) {
|
|
pixel rp = cur_ref->get_pixel(i, j);
|
|
int d; ale_pos div;
|
|
for (d = 1, div = 2; ; d++, div *= 2) {
|
|
ale_pos height_scale = orig_ref_height / div;
|
|
ale_pos width_scale = orig_ref_width / div;
|
|
multi_coordinate c;
|
|
c.degree = d;
|
|
c.y = floor((cur_offset[0] - orig_offset[0] + i) / height_scale);
|
|
c.x = floor((cur_offset[1] - orig_offset[1] + j) / width_scale);
|
|
if (!coordinate_map.count(c))
|
|
break;
|
|
index_t index = coordinate_map[c];
|
|
|
|
check_multi(i, j, rp, cur_ref, input, index);
|
|
}
|
|
}
|
|
}
|
|
|
|
void fill_multi_init(unsigned char *update_map) {
|
|
for (unsigned int l = 0; l < coord_stack.size(); l++) {
|
|
elem_bounds_int_t b = elem_bounds(l).scale_to_bounds(cur_ref_height, cur_ref_width);
|
|
|
|
for (unsigned int i = b.imin; i < b.imax; i++) {
|
|
update_map[i * cur_ref_width + b.jmin] |= (1 | 2 | 4);
|
|
update_map[i * cur_ref_width + (b.jmax - 1)] |= (8 | 16 | 32);
|
|
}
|
|
|
|
for (unsigned int j = b.jmin; j < b.jmax; j++) {
|
|
update_map[b.imin * cur_ref_width + j] |= (1 | 64 | 8);
|
|
update_map[(b.imax - 1) * cur_ref_width + j] |= (4 | 128 | 32);
|
|
}
|
|
}
|
|
|
|
for (unsigned int i = 0; i < cur_ref_height; i++)
|
|
update_map[cur_ref_width * cur_ref_height + i] = 1;
|
|
for (unsigned int j = 0; j < cur_ref_width; j++)
|
|
update_map[cur_ref_width * cur_ref_height + cur_ref_height + j] = 1;
|
|
}
|
|
|
|
int step_fill_multi(unsigned char *update_map, const image *cur_ref, const image *input) {
|
|
if (cur_ref_height == 0
|
|
|| cur_ref_width == 0)
|
|
return 0;
|
|
|
|
unsigned int i_min, i_max, j_min, j_max;
|
|
int result = 0;
|
|
|
|
i_min = cur_ref_height;
|
|
i_max = cur_ref_height;
|
|
j_min = cur_ref_width;
|
|
j_max = cur_ref_width;
|
|
|
|
for (unsigned int i = 0; i < cur_ref_height; i++)
|
|
if (update_map[cur_ref_width * cur_ref_height + i]) {
|
|
i_min = i;
|
|
i_max = i + 1;
|
|
break;
|
|
}
|
|
for (unsigned int i = cur_ref_height - 1; i >= i_max; i--)
|
|
if (update_map[cur_ref_width * cur_ref_height + i]) {
|
|
i_max = i + 1;
|
|
break;
|
|
}
|
|
for (unsigned int j = 0; j < cur_ref_width; j++)
|
|
if (update_map[cur_ref_width * cur_ref_height + cur_ref_height + j]) {
|
|
j_min = j;
|
|
j_max = j + 1;
|
|
break;
|
|
}
|
|
for (unsigned int j = cur_ref_width - 1; j >= j_max; j--)
|
|
if (update_map[cur_ref_width * cur_ref_height + cur_ref_height + j]) {
|
|
j_max = j + 1;
|
|
break;
|
|
}
|
|
|
|
if (!(i_min < i_max) || !(j_min < j_max))
|
|
return 0;
|
|
|
|
for (unsigned int i = i_min; i < i_max; i++)
|
|
update_map[cur_ref_width * cur_ref_height + i] = 0;
|
|
for (unsigned int j = j_min; j < j_max; j++)
|
|
update_map[cur_ref_width * cur_ref_height + cur_ref_height + j] = 0;
|
|
|
|
for (unsigned int i = i_min; i < i_max; i++)
|
|
for (unsigned int j = j_min; j < j_max; j++) {
|
|
int o = cur_ref_width * i + j;
|
|
|
|
if (!update_map[o])
|
|
continue;
|
|
|
|
pixel rp = cur_ref->get_pixel(i, j);
|
|
|
|
int n = o - cur_ref_width;
|
|
int s = o + cur_ref_width;
|
|
int e = o + 1;
|
|
int w = o - 1;
|
|
int ne = n + 1;
|
|
int nw = n - 1;
|
|
int se = s + 1;
|
|
int sw = s - 1;
|
|
int dirs[8] = {
|
|
nw, w, sw,
|
|
ne, e, se,
|
|
n, s
|
|
};
|
|
int comp_dirs[8] = {
|
|
5, 4, 3,
|
|
2, 1, 0,
|
|
7, 6
|
|
};
|
|
|
|
for (int di = 0; di < 8; di++) {
|
|
if (!(update_map[o] & (1 << di)))
|
|
continue;
|
|
|
|
int d = dirs[di];
|
|
|
|
if (d < 0 || (unsigned int) d >= cur_ref_width * cur_ref_height)
|
|
continue;
|
|
|
|
if (spatio_elem_map[d] == spatio_elem_map[o])
|
|
continue;
|
|
|
|
int changed = check_multi(i, j, rp, cur_ref, input, spatio_elem_map[d]);
|
|
|
|
if (!changed)
|
|
continue;
|
|
|
|
for (int ddi = 0; ddi < 8; ddi++) {
|
|
int dd = dirs[ddi];
|
|
|
|
if (dd < 0 || (unsigned int) dd >= cur_ref_width * cur_ref_height)
|
|
continue;
|
|
|
|
if (spatio_elem_map[dd] == spatio_elem_map[o])
|
|
continue;
|
|
|
|
result |= 1;
|
|
|
|
update_map[dd] |= (1 << comp_dirs[ddi]);
|
|
|
|
update_map[cur_ref_height * cur_ref_width
|
|
+ dd / cur_ref_width] = 1;
|
|
update_map[cur_ref_height * cur_ref_width
|
|
+ cur_ref_height + dd % cur_ref_width] = 1;
|
|
}
|
|
}
|
|
|
|
update_map[o] = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void fill_multi(const image *cur_ref, const image *input) {
|
|
unsigned char *update_map = (unsigned char *) calloc(
|
|
cur_ref_height * cur_ref_width
|
|
+ cur_ref_height + cur_ref_width,
|
|
sizeof(unsigned char));
|
|
|
|
fill_multi_init(update_map);
|
|
|
|
while (step_fill_multi(update_map, cur_ref, input));
|
|
|
|
free(update_map);
|
|
}
|
|
|
|
public:
|
|
void set_multi(const image *cur_ref, const image *input) {
|
|
assert(use_multi == 0);
|
|
assert(spatio_elem_map == NULL);
|
|
assert(spatio_elem_map_r == NULL);
|
|
use_multi = 1;
|
|
|
|
spatio_elem_map = (index_t *) calloc(
|
|
cur_ref_height * cur_ref_width, sizeof(index_t));
|
|
assert(spatio_elem_map);
|
|
|
|
spatio_elem_map_r = (index_t *) calloc(
|
|
input_height * input_width, sizeof(index_t));
|
|
assert(spatio_elem_map_r);
|
|
|
|
if (_multi == 4) {
|
|
assign_multi_global_best(cur_ref, input);
|
|
} else {
|
|
assign_multi_best(cur_ref, input);
|
|
|
|
if (_multi == 2)
|
|
fill_multi(cur_ref, input);
|
|
}
|
|
|
|
/*
|
|
* All scale factors should be identical.
|
|
*/
|
|
scale_factor = trans_stack[0].scale();
|
|
}
|
|
|
|
/*
|
|
* Returns non-zero if the transformation might be non-Euclidean.
|
|
*/
|
|
int is_projective() const {
|
|
return trans_stack[current_element].is_projective();
|
|
}
|
|
|
|
/*
|
|
* Transformation at point in the domain
|
|
*/
|
|
trans_single t_at_point(struct point p) const {
|
|
if (!use_multi)
|
|
return trans_stack[current_element];
|
|
|
|
int ii = (int) p[0];
|
|
int jj = (int) p[1];
|
|
|
|
if (ii < 0 || (unsigned int) ii >= input_height
|
|
|| jj < 0 || (unsigned int) jj >= input_width)
|
|
return trans_stack[0];
|
|
|
|
return trans_stack[spatio_elem_map_r[input_width * ii + jj]];
|
|
}
|
|
|
|
/*
|
|
* Transformation at point in the co-domain.
|
|
*/
|
|
trans_single t_at_inv_point(struct point p) const {
|
|
if (!use_multi)
|
|
return trans_stack[current_element];
|
|
|
|
int i = (int) (p[0] - cur_offset[0]);
|
|
int j = (int) (p[1] - cur_offset[1]);
|
|
|
|
if (i < 0 || (unsigned int) i >= cur_ref_height
|
|
|| j < 0 || (unsigned int) j >= cur_ref_width)
|
|
return trans_stack[0];
|
|
|
|
return trans_stack[spatio_elem_map[cur_ref_width * i + j]];
|
|
}
|
|
|
|
/*
|
|
* Projective/Euclidean transformations
|
|
*/
|
|
struct point pe(struct point p) const {
|
|
if (!use_multi)
|
|
return trans_stack[current_element].pe(p);
|
|
|
|
int ii = (int) p[0];
|
|
int jj = (int) p[1];
|
|
|
|
if (ii < 0 || (unsigned int) ii >= input_height
|
|
|| jj < 0 || (unsigned int) jj >= input_width)
|
|
return trans_stack[0].pe(p);
|
|
|
|
return trans_stack[spatio_elem_map_r[input_width * ii + jj]].pe(p);
|
|
}
|
|
|
|
/*
|
|
* Inverse transformations
|
|
*/
|
|
struct point pei(struct point p) const {
|
|
if (!use_multi)
|
|
return trans_stack[current_element].pei(p);
|
|
|
|
int i = (int) (p[0] - cur_offset[0]);
|
|
int j = (int) (p[1] - cur_offset[1]);
|
|
|
|
if (i < 0 || (unsigned int) i >= cur_ref_height
|
|
|| j < 0 || (unsigned int) j >= cur_ref_width)
|
|
return trans_stack[0].pei(p);
|
|
|
|
return trans_stack[spatio_elem_map[cur_ref_width * i + j]].pei(p);
|
|
}
|
|
|
|
pixel get_tonal_multiplier(struct point p) const {
|
|
if (!use_multi)
|
|
return trans_stack[current_element].get_tonal_multiplier(p);
|
|
|
|
int i = (int) (p[0] - cur_offset[0]);
|
|
int j = (int) (p[1] - cur_offset[1]);
|
|
|
|
if (i < 0 || (unsigned int) i >= cur_ref_height
|
|
|| j < 0 || (unsigned int) j >= cur_ref_width)
|
|
return trans_stack[0].get_tonal_multiplier(p);
|
|
|
|
return trans_stack[spatio_elem_map[cur_ref_width * i + j]].get_tonal_multiplier(p);
|
|
}
|
|
|
|
pixel get_inverse_tonal_multiplier(struct point p) const {
|
|
if (!use_multi)
|
|
return trans_stack[current_element].get_inverse_tonal_multiplier(p);
|
|
|
|
int i = (int) p[0];
|
|
int j = (int) p[1];
|
|
|
|
if (i < 0 || (unsigned int) i >= input_height
|
|
|| j < 0 || (unsigned int) j >= input_width)
|
|
return trans_stack[0].get_inverse_tonal_multiplier(p);
|
|
|
|
return trans_stack[spatio_elem_map_r[input_width * i + j]].get_inverse_tonal_multiplier(p);
|
|
}
|
|
|
|
void set_tonal_multiplier(pixel p) {
|
|
trans_stack[current_element].set_tonal_multiplier(p);
|
|
}
|
|
|
|
/*
|
|
* Modify a euclidean transform in the indicated manner.
|
|
*/
|
|
void eu_modify(int i1, ale_pos diff) {
|
|
trans_stack[current_element].eu_modify(i1, diff);
|
|
}
|
|
|
|
/*
|
|
* Rotate about a given point in the original reference frame.
|
|
*/
|
|
void eu_rotate_about_scaled(point center, ale_pos diff) {
|
|
trans_stack[current_element].eu_rotate_about_scaled(center, diff);
|
|
}
|
|
|
|
/*
|
|
* Modify all euclidean parameters at once.
|
|
*/
|
|
void eu_set(ale_pos eu[3]) {
|
|
trans_stack[current_element].eu_set(eu);
|
|
}
|
|
|
|
/*
|
|
* Get the specified euclidean parameter
|
|
*/
|
|
ale_pos eu_get(int param) const {
|
|
return trans_stack[current_element].eu_get(param);
|
|
}
|
|
|
|
/*
|
|
* Modify a projective transform in the indicated manner.
|
|
*/
|
|
void gpt_modify(int i1, int i2, ale_pos diff) {
|
|
trans_stack[current_element].gpt_modify(i1, i2, diff);
|
|
}
|
|
|
|
/*
|
|
* Modify a projective transform according to the group operation.
|
|
*/
|
|
void gr_modify(int i1, int i2, ale_pos diff) {
|
|
trans_stack[current_element].gr_modify(i1, i2, diff);
|
|
}
|
|
|
|
/*
|
|
* Modify all projective parameters at once.
|
|
*/
|
|
void gpt_set(point x[4]) {
|
|
trans_stack[current_element].gpt_set(x);
|
|
}
|
|
|
|
void gpt_set(point x1, point x2, point x3, point x4) {
|
|
trans_stack[current_element].gpt_set(x1, x2, x3, x4);
|
|
}
|
|
|
|
void snap(ale_pos interval) {
|
|
trans_stack[current_element].snap(interval);
|
|
}
|
|
|
|
/*
|
|
* Get the specified projective parameter
|
|
*/
|
|
point gpt_get(int point) const {
|
|
return trans_stack[current_element].gpt_get(point);
|
|
}
|
|
|
|
/*
|
|
* Get the specified projective parameter
|
|
*/
|
|
ale_pos gpt_get(int point, int dim) {
|
|
return trans_stack[current_element].gpt_get(point, dim);
|
|
}
|
|
|
|
/*
|
|
* Translate by a given amount
|
|
*/
|
|
void translate(point p) {
|
|
trans_stack[current_element].translate(p);
|
|
}
|
|
|
|
/*
|
|
* Rotate by a given amount about a given point.
|
|
*/
|
|
void rotate(point p, ale_pos degrees) {
|
|
trans_stack[current_element].rotate(p, degrees);
|
|
}
|
|
|
|
void reset_memos() {
|
|
for (unsigned int t = 0; t < trans_stack.size(); t++)
|
|
trans_stack[t].reset_memos();
|
|
}
|
|
|
|
/*
|
|
* Rescale a transform with a given factor.
|
|
*/
|
|
void specific_rescale(ale_pos factor) {
|
|
|
|
/*
|
|
* Ensure that no maps exist.
|
|
*/
|
|
|
|
assert (use_multi == 0);
|
|
assert (spatio_elem_map == NULL);
|
|
assert (spatio_elem_map_r == NULL);
|
|
|
|
trans_stack[current_element].rescale(factor);
|
|
}
|
|
|
|
/*
|
|
* Set the dimensions of the image.
|
|
*/
|
|
void specific_set_dimensions(const image *im) {
|
|
for (unsigned int t = 0; t < trans_stack.size(); t++)
|
|
trans_stack[t].set_dimensions(im);
|
|
}
|
|
|
|
void map_area(point p, point *q, ale_pos d[2]) {
|
|
t_at_point(p / scale_factor).map_area(p, q, d);
|
|
}
|
|
|
|
void map_area_unscaled(point p, point *q, ale_pos d[2]) {
|
|
t_at_point(p).map_area_unscaled(p, q, d);
|
|
}
|
|
|
|
void unscaled_map_area_inverse(point p, point *q, ale_pos d[2]) {
|
|
t_at_inv_point(p).unscaled_map_area_inverse(p, q, d);
|
|
}
|
|
|
|
/*
|
|
* 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]) {
|
|
trans_stack[current_element].gpt_v0_set(x);
|
|
}
|
|
|
|
/*
|
|
* 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]) {
|
|
trans_stack[current_element].eu_v0_set(eu);
|
|
}
|
|
|
|
void debug_output() {
|
|
for (unsigned int t = 0; t < trans_stack.size(); t++)
|
|
trans_stack[t].debug_output();
|
|
}
|
|
};
|
|
|
|
#endif
|