1121 lines
26 KiB
C++
1121 lines
26 KiB
C++
// 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.h: Abstract base class for the internal representations of images used
|
|
* by ALE.
|
|
*/
|
|
|
|
#ifndef __image_h__
|
|
#define __image_h__
|
|
|
|
#include "point.h"
|
|
#include "pixel.h"
|
|
#include "exposure/exposure.h"
|
|
|
|
#define IMAGE_BAYER_NONE 0
|
|
|
|
/*
|
|
* This constant indicates that some other default value should be filled in.
|
|
*/
|
|
|
|
#define IMAGE_BAYER_DEFAULT 0x8
|
|
|
|
/*
|
|
* Do not change these values without inspecting
|
|
* image_bayer_ale_real::r_*_offset().
|
|
*/
|
|
#define IMAGE_BAYER_RGBG 0x4 /* binary 100 */
|
|
#define IMAGE_BAYER_GBGR 0x5 /* binary 101 */
|
|
#define IMAGE_BAYER_GRGB 0x6 /* binary 110 */
|
|
#define IMAGE_BAYER_BGRG 0x7 /* binary 111 */
|
|
|
|
class image : protected exposure::listener {
|
|
protected:
|
|
static double resident;
|
|
unsigned int _dimx, _dimy, _depth;
|
|
point _offset;
|
|
const char *name;
|
|
mutable exposure *_exp;
|
|
unsigned int bayer;
|
|
private:
|
|
/*
|
|
* Memoized function variables. We may want to change these even when
|
|
* *this is constant.
|
|
*/
|
|
mutable int _apm_memo;
|
|
mutable ale_real _apm;
|
|
mutable int _accm_memo;
|
|
mutable pixel _accm;
|
|
mutable int _acm_memo;
|
|
mutable pixel _acm;
|
|
|
|
void avg_channel_clamped_magnitude_memo() const {
|
|
unsigned int i, j, k;
|
|
pixel_accum accumulator;
|
|
pixel_accum divisor;
|
|
|
|
if (_accm_memo)
|
|
return;
|
|
|
|
_accm_memo = 1;
|
|
|
|
accumulator = pixel_accum(0, 0, 0);
|
|
|
|
for (i = 0; i < _dimy; i++)
|
|
for (j = 0; j < _dimx; j++) {
|
|
pixel value = get_pixel(i, j);
|
|
|
|
for (k = 0; k < _depth; k++)
|
|
if (finite(value[k])) {
|
|
if (value[k] > 1)
|
|
value[k] = 1;
|
|
if (value[k] < 0)
|
|
value[k] = 0;
|
|
accumulator[k] += value[k];
|
|
divisor[k] += 1;
|
|
}
|
|
}
|
|
|
|
accumulator /= divisor;
|
|
|
|
_accm = accumulator;
|
|
}
|
|
|
|
void avg_channel_magnitude_memo() const {
|
|
unsigned int i, j, k;
|
|
pixel_accum accumulator;
|
|
pixel_accum divisor;
|
|
|
|
if (_acm_memo)
|
|
return;
|
|
|
|
_acm_memo = 1;
|
|
|
|
accumulator = pixel_accum(0, 0, 0);
|
|
|
|
for (i = 0; i < _dimy; i++)
|
|
for (j = 0; j < _dimx; j++) {
|
|
pixel value = get_pixel(i, j);
|
|
|
|
for (k = 0; k < _depth; k++)
|
|
if (finite(value[k])) {
|
|
accumulator[k] += value[k];
|
|
divisor[k] += 1;
|
|
}
|
|
}
|
|
|
|
accumulator /= divisor;
|
|
|
|
_acm = accumulator;
|
|
}
|
|
|
|
protected:
|
|
void image_updated() {
|
|
_apm_memo = 0;
|
|
_acm_memo = 0;
|
|
_accm_memo = 0;
|
|
}
|
|
|
|
public:
|
|
static void set_resident(double r) {
|
|
resident = r;
|
|
}
|
|
|
|
static double get_resident() {
|
|
return resident;
|
|
}
|
|
|
|
image (unsigned int dimy, unsigned int dimx, unsigned int depth,
|
|
const char *name = "anonymous", exposure *_exp = NULL,
|
|
unsigned int bayer = IMAGE_BAYER_NONE) {
|
|
|
|
assert (depth == 3);
|
|
_depth = 3;
|
|
|
|
_dimx = dimx;
|
|
_dimy = dimy;
|
|
_offset = point(0, 0);
|
|
_apm_memo = 0;
|
|
_acm_memo = 0;
|
|
_accm_memo = 0;
|
|
this->name = name;
|
|
this->_exp = _exp;
|
|
this->bayer = bayer;
|
|
|
|
if (_exp != NULL)
|
|
_exp->add_listener(this, name);
|
|
}
|
|
|
|
unsigned int get_bayer() const {
|
|
return bayer;
|
|
}
|
|
|
|
virtual char get_channels(int i, int j) const {
|
|
return 0x7;
|
|
}
|
|
|
|
virtual unsigned int bayer_color(unsigned int i, unsigned int j) const {
|
|
assert(0);
|
|
}
|
|
|
|
double storage_size() const {
|
|
if (bayer != IMAGE_BAYER_NONE)
|
|
return _dimx * _dimy * sizeof(ale_real);
|
|
|
|
return 3 * _dimx * _dimy * sizeof(ale_real);
|
|
}
|
|
|
|
exposure &exp() const {
|
|
return *_exp;
|
|
}
|
|
|
|
point offset() const {
|
|
return _offset;
|
|
}
|
|
|
|
void set_offset(int i, int j) {
|
|
_offset[0] = i;
|
|
_offset[1] = j;
|
|
}
|
|
|
|
void set_offset(point p) {
|
|
_offset = p;
|
|
}
|
|
|
|
unsigned int width() const {
|
|
return _dimx;
|
|
}
|
|
|
|
unsigned int height() const {
|
|
return _dimy;
|
|
}
|
|
|
|
unsigned int depth() const {
|
|
return _depth;
|
|
}
|
|
|
|
virtual void set_pixel(unsigned int y, unsigned int x, spixel p) = 0;
|
|
|
|
virtual spixel get_pixel(unsigned int y, unsigned int x) const = 0;
|
|
|
|
virtual spixel get_raw_pixel(unsigned int y, unsigned int x) const {
|
|
return ((const image *)this)->get_pixel(y, x);
|
|
}
|
|
|
|
virtual void add_pixel(unsigned int y, unsigned int x, pixel p) {
|
|
assert(0);
|
|
}
|
|
|
|
virtual void mul_pixel(unsigned int y, unsigned int x, pixel p) {
|
|
assert(0);
|
|
}
|
|
|
|
virtual void div_pixel(unsigned int y, unsigned int x, pixel p) {
|
|
assert(0);
|
|
}
|
|
|
|
virtual void add_chan(unsigned int y, unsigned int x, unsigned int k, ale_real c) {
|
|
assert(0);
|
|
}
|
|
|
|
virtual void div_chan(unsigned int y, unsigned int x, unsigned int k, ale_real c) {
|
|
assert(0);
|
|
}
|
|
|
|
virtual void set_chan(unsigned int y, unsigned int x, unsigned int k, ale_sreal c) = 0;
|
|
|
|
virtual ale_sreal get_chan(unsigned int y, unsigned int x, unsigned int k) const = 0;
|
|
|
|
ale_real maxval() const {
|
|
ale_real result = get_pixel(0, 0)[0];
|
|
|
|
for (unsigned int i = 0; i < _dimy; i++)
|
|
for (unsigned int j = 0; j < _dimx; j++) {
|
|
pixel p = get_pixel(i, j);
|
|
|
|
for (unsigned int k = 0; k < _depth; k++)
|
|
if (p[k] > result || !finite(result))
|
|
result = p[k];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
ale_real minval() const {
|
|
ale_real result = get_pixel(0, 0)[0];
|
|
|
|
for (unsigned int i = 0; i < _dimy; i++)
|
|
for (unsigned int j = 0; j < _dimx; j++) {
|
|
pixel p = get_pixel(i, j);
|
|
|
|
for (unsigned int k = 0; k < _depth; k++)
|
|
if (p[k] < result || !finite(result))
|
|
result = p[k];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Get the maximum difference among adjacent pixels.
|
|
*/
|
|
|
|
pixel get_max_diff(unsigned int i, unsigned int j) const {
|
|
assert(i <= _dimy - 1);
|
|
assert(j <= _dimx - 1);
|
|
|
|
pixel max = get_pixel(i, j), min = get_pixel(i, j);
|
|
|
|
for (int ii = -1; ii <= 1; ii++)
|
|
for (int jj = -1; jj <= 1; jj++) {
|
|
int iii = i + ii;
|
|
int jjj = j + jj;
|
|
|
|
if (iii < 0)
|
|
continue;
|
|
if (jjj < 0)
|
|
continue;
|
|
if ((unsigned int) iii > _dimy - 1)
|
|
continue;
|
|
if ((unsigned int) jjj > _dimx - 1)
|
|
continue;
|
|
|
|
pixel p = get_pixel(iii, jjj);
|
|
|
|
for (int d = 0; d < 3; d++) {
|
|
if (p[d] > max[d])
|
|
max[d] = p[d];
|
|
if (p[d] < min[d])
|
|
min[d] = p[d];
|
|
}
|
|
}
|
|
|
|
return max - min;
|
|
}
|
|
|
|
pixel get_max_diff(point x) const {
|
|
assert (x[0] >= 0);
|
|
assert (x[1] >= 0);
|
|
assert (x[0] <= _dimy - 1);
|
|
assert (x[1] <= _dimx - 1);
|
|
|
|
unsigned int i = (unsigned int) round(x[0]);
|
|
unsigned int j = (unsigned int) round(x[1]);
|
|
|
|
return get_max_diff(i, j);
|
|
}
|
|
|
|
int in_bounds(point x) const {
|
|
if (x[0] < 0
|
|
|| x[1] < 0
|
|
|| x[0] > height() - 1
|
|
|| x[1] > width() - 1)
|
|
return 0;
|
|
if (!x.defined())
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Get a color value at a given position using bilinear interpolation between the
|
|
* four nearest pixels.
|
|
*/
|
|
pixel get_bl(point x, int defined = 0) const {
|
|
// fprintf(stderr, "get_bl x=%f %f\n", (double) x[0], (double) x[1]);
|
|
|
|
pixel result;
|
|
|
|
assert (x[0] >= 0);
|
|
assert (x[1] >= 0);
|
|
assert (x[0] <= _dimy - 1);
|
|
assert (x[1] <= _dimx - 1);
|
|
|
|
int lx = (int) floor(x[1]);
|
|
int hx = (int) floor(x[1]) + 1;
|
|
int ly = (int) floor(x[0]);
|
|
int hy = (int) floor(x[0]) + 1;
|
|
|
|
// fprintf(stderr, "get_bl l=%d %d h=%d %d\n", ly, lx, hy, hx);
|
|
|
|
pixel neighbor[4];
|
|
ale_real factor[4];
|
|
|
|
neighbor[0] = get_pixel(ly, lx);
|
|
neighbor[1] = get_pixel(hy % _dimy, lx);
|
|
neighbor[2] = get_pixel(hy % _dimy, hx % _dimx);
|
|
neighbor[3] = get_pixel(ly, hx % _dimx);
|
|
|
|
// for (int d = 0; d < 4; d++)
|
|
// fprintf(stderr, "neighbor_%d=%f %f %f\n", d,
|
|
// (double) neighbor[d][0],
|
|
// (double) neighbor[d][1],
|
|
// (double) neighbor[d][2]);
|
|
|
|
factor[0] = (ale_real) (hx - x[1]) * (ale_real) (hy - x[0]);
|
|
factor[1] = (ale_real) (hx - x[1]) * (ale_real) (x[0] - ly);
|
|
factor[2] = (ale_real) (x[1] - lx) * (ale_real) (x[0] - ly);
|
|
factor[3] = (ale_real) (x[1] - lx) * (ale_real) (hy - x[0]);
|
|
|
|
// for (int d = 0; d < 4; d++)
|
|
// fprintf(stderr, "factor_%d=%f\n", d,
|
|
// (double) factor[d]);
|
|
|
|
/*
|
|
* Use bilinear and/or geometric interpolation
|
|
*/
|
|
|
|
if (defined == 0) {
|
|
result = pixel(0, 0, 0);
|
|
|
|
for (int n = 0; n < 4; n++)
|
|
result += factor[n] * neighbor[n];
|
|
} else {
|
|
#if 0
|
|
/*
|
|
* Calculating the geometric mean may be expensive on
|
|
* some platforms (e.g., those without floating-point
|
|
* support.
|
|
*/
|
|
|
|
result = pixel(1, 1, 1);
|
|
|
|
for (int n = 0; n < 4; n++)
|
|
result *= ppow(neighbor[n], factor[n]);
|
|
#else
|
|
/*
|
|
* Taking the minimum value may be cheaper than
|
|
* calculating a geometric mean.
|
|
*/
|
|
|
|
result = neighbor[0];
|
|
|
|
for (int n = 1; n < 4; n++)
|
|
for (int k = 0; k < 3; k++)
|
|
if (neighbor[n][k] < result[k])
|
|
result[k] = neighbor[n][k];
|
|
#endif
|
|
}
|
|
|
|
// fprintf(stderr, "result=%f %f %f\n",
|
|
// (double) result[0],
|
|
// (double) result[1],
|
|
// (double) result[2]);
|
|
|
|
return result;
|
|
}
|
|
|
|
pixel get_scaled_bl(point x, ale_pos f, int defined = 0) const {
|
|
point scaled(
|
|
x[0]/f <= height() - 1
|
|
? (x[0]/f)
|
|
: (ale_pos) (height() - 1),
|
|
x[1]/f <= width() - 1
|
|
? (x[1]/f)
|
|
: (ale_pos) (width() - 1));
|
|
|
|
return get_bl(scaled, defined);
|
|
}
|
|
|
|
|
|
/*
|
|
* Make a new image suitable for receiving scaled values.
|
|
*/
|
|
virtual image *scale_generator(int height, int width, int depth, const char *name) const = 0;
|
|
|
|
/*
|
|
* Generate an image of medians within a given radius
|
|
*/
|
|
|
|
image *medians(int radius) const {
|
|
|
|
assert (radius >= 0);
|
|
|
|
image *is = scale_generator(height(), width(), depth(), "median");
|
|
assert(is);
|
|
|
|
for (unsigned int i = 0; i < height(); i++)
|
|
for (unsigned int j = 0; j < width(); j++) {
|
|
|
|
std::vector<ale_real> p[3];
|
|
|
|
for (int ii = -radius; ii <= radius; ii++)
|
|
for (int jj = -radius; jj <= radius; jj++) {
|
|
int iii = i + ii;
|
|
int jjj = j + jj;
|
|
|
|
if (in_bounds(point(iii, jjj)))
|
|
for (int k = 0; k < 3; k++)
|
|
if (finite(get_pixel(iii, jjj)[k]))
|
|
p[k].push_back(get_pixel(iii, jjj)[k]);
|
|
}
|
|
|
|
is->set_pixel(i, j, d2::pixel::undefined());
|
|
|
|
for (int k = 0; k < 3; k++) {
|
|
std::sort(p[k].begin(), p[k].end());
|
|
|
|
unsigned int pkc = p[k].size();
|
|
|
|
if (pkc == 0)
|
|
continue;
|
|
|
|
if (pkc % 2 == 0)
|
|
is->set_chan(i, j, k,
|
|
(p[k][pkc / 2] + p[k][pkc / 2 - 1]) / 2);
|
|
else
|
|
is->set_chan(i, j, k,
|
|
p[k][pkc / 2]);
|
|
}
|
|
}
|
|
|
|
return is;
|
|
}
|
|
|
|
/*
|
|
* Generate an image of differences of the first channel. The first
|
|
* coordinate differences are stored in the first channel, second in the
|
|
* second channel.
|
|
*/
|
|
|
|
image *fcdiffs() const {
|
|
image *is = scale_generator(height(), width(), depth(), "diff");
|
|
|
|
assert(is);
|
|
|
|
for (unsigned int i = 0; i < height(); i++)
|
|
for (unsigned int j = 0; j < width(); j++) {
|
|
|
|
if (i + 1 < height()
|
|
&& i > 0
|
|
&& !finite(get_chan(i, j, 0))) {
|
|
|
|
is->set_chan(i, j, 0, (get_chan(i + 1, j, 0)
|
|
- get_chan(i - 1, j, 0)) / 2);
|
|
|
|
} else if (i + 1 < height()
|
|
&& i > 0
|
|
&& finite(get_chan(i + 1, j, 0))
|
|
&& finite(get_chan(i - 1, j, 0))) {
|
|
|
|
is->set_chan(i, j, 0, ((get_chan(i, j, 0) - get_chan(i - 1, j, 0))
|
|
+ (get_chan(i + 1, j, 0) - get_chan(i, j, 0))) / 2);
|
|
|
|
} else if (i + 1 < height()
|
|
&& finite(get_chan(i + 1, j, 0))) {
|
|
|
|
is->set_chan(i, j, 0, get_chan(i + 1, j, 0) - get_chan(i, j, 0));
|
|
|
|
} else if (i > 0
|
|
&& finite(get_chan(i - 1, j, 0))) {
|
|
|
|
is->set_chan(i, j, 0, get_chan(i, j, 0) - get_chan(i - 1, j, 0));
|
|
|
|
} else {
|
|
is->set_chan(i, j, 0, 0);
|
|
}
|
|
|
|
if (j + 1 < width()
|
|
&& j > 0
|
|
&& !finite(get_chan(i, j, 0))) {
|
|
|
|
is->set_chan(i, j, 1, (get_chan(i, j + 1, 0) - get_chan(i, j - 1, 0)) / 2);
|
|
|
|
} else if (j + 1 < width()
|
|
&& j > 0
|
|
&& finite(get_chan(i, j + 1, 0))
|
|
&& finite(get_chan(i, j - 1, 0))) {
|
|
|
|
is->set_chan(i, j, 1, ((get_chan(i, j, 0) - get_chan(i, j - 1, 0))
|
|
+ (get_chan(i, j + 1, 0) - get_chan(i, j, 0))) / 2);
|
|
|
|
} else if (j + 1 < width() && finite(get_chan(i, j + 1, 0))) {
|
|
|
|
is->set_chan(i, j, 1, get_chan(i, j + 1, 0) - get_chan(i, j, 0));
|
|
|
|
} else if (j > 0 && finite(get_chan(i, j - 1, 0))) {
|
|
|
|
is->set_chan(i, j, 1, get_chan(i, j, 0) - get_chan(i, j - 1, 0));
|
|
|
|
} else {
|
|
is->set_chan(i, j, 1, 0);
|
|
}
|
|
}
|
|
|
|
return is;
|
|
}
|
|
|
|
/*
|
|
* Generate an image of median (within a given radius) difference of the
|
|
* first channel.
|
|
*/
|
|
|
|
image *fcdiff_median(int radius) const {
|
|
image *diff = fcdiffs();
|
|
|
|
assert(diff);
|
|
|
|
image *median = diff->medians(radius);
|
|
|
|
assert(median);
|
|
|
|
delete diff;
|
|
|
|
return median;
|
|
}
|
|
|
|
/*
|
|
* 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 the
|
|
* weights of contributing pixels is 1.
|
|
*/
|
|
class scale_by_half_threaded : public thread::decompose_domain {
|
|
image *is;
|
|
const image *iu;
|
|
protected:
|
|
void subdomain_algorithm(unsigned int thread,
|
|
int i_min, int i_max, int j_min, int j_max) {
|
|
|
|
ale_real _0625 = (ale_real) 0.0625;
|
|
ale_real _125 = (ale_real) 0.125;
|
|
ale_real _25 = (ale_real) 0.25;
|
|
ale_real _0 = (ale_real) 0;
|
|
|
|
unsigned int ui_min = (unsigned int) i_min;
|
|
unsigned int ui_max = (unsigned int) i_max;
|
|
unsigned int uj_min = (unsigned int) j_min;
|
|
unsigned int uj_max = (unsigned int) j_max;
|
|
|
|
for (unsigned int i = ui_min; i < ui_max; i++)
|
|
for (unsigned int j = uj_min; j < uj_max; j++) {
|
|
is->set_pixel(i, j,
|
|
|
|
( ( ((i > 0 && j > 0)
|
|
? iu->get_pixel(2 * i - 1, 2 * j - 1) * _0625
|
|
: pixel(0, 0, 0))
|
|
+ ((i > 0)
|
|
? iu->get_pixel(2 * i - 1, 2 * j) * _125
|
|
: pixel(0, 0, 0))
|
|
+ ((i > 0 && j < is->width() - 1)
|
|
? iu->get_pixel(2 * i - 1, 2 * j + 1) * _0625
|
|
: pixel(0, 0, 0))
|
|
+ ((j > 0)
|
|
? iu->get_pixel(2 * i, 2 * j - 1) * _125
|
|
: pixel(0, 0, 0))
|
|
+ iu->get_pixel(2 * i, 2 * j) * _25
|
|
+ ((j < is->width() - 1)
|
|
? iu->get_pixel(2 * i, 2 * j + 1) * _125
|
|
: pixel(0, 0, 0))
|
|
+ ((i < is->height() - 1 && j > 0)
|
|
? iu->get_pixel(2 * i + 1, 2 * j - 1) * _0625
|
|
: pixel(0, 0, 0))
|
|
+ ((i < is->height() - 1)
|
|
? iu->get_pixel(2 * i + 1, 2 * j) * _125
|
|
: pixel(0, 0, 0))
|
|
+ ((i < is->height() && j < is->width() - 1)
|
|
? iu->get_pixel(2 * i + 1, 2 * j + 1) * _0625
|
|
: pixel(0, 0, 0)))
|
|
|
|
/
|
|
|
|
( ((i > 0 && j > 0)
|
|
? _0625
|
|
: _0)
|
|
+ ((i > 0)
|
|
? _125
|
|
: _0)
|
|
+ ((i > 0 && j < is->width() - 1)
|
|
? _0625
|
|
: _0)
|
|
+ ((j > 0)
|
|
? _125
|
|
: _0)
|
|
+ _25
|
|
+ ((j < is->width() - 1)
|
|
? _125
|
|
: _0)
|
|
+ ((i < is->height() - 1 && j > 0)
|
|
? _0625
|
|
: _0)
|
|
+ ((i < is->height() - 1)
|
|
? _125
|
|
: _0)
|
|
+ ((i < is->height() && j < is->width() - 1)
|
|
? _0625
|
|
: _0) ) ) );
|
|
}
|
|
}
|
|
|
|
public:
|
|
scale_by_half_threaded(image *_is, const image *_iu)
|
|
: decompose_domain(0, _is->height(),
|
|
0, _is->width()) {
|
|
is = _is;
|
|
iu = _iu;
|
|
}
|
|
};
|
|
|
|
image *scale_by_half(const char *name) const {
|
|
ale_pos f = 0.5;
|
|
|
|
image *is = scale_generator(
|
|
(int) floor(height() * (double) f),
|
|
(int) floor(width() * (double) f), depth(), name);
|
|
|
|
assert(is);
|
|
|
|
scale_by_half_threaded sbht(is, this);
|
|
sbht.run();
|
|
|
|
is->_offset = point(_offset[0] * f, _offset[1] * f);
|
|
|
|
return is;
|
|
}
|
|
|
|
/*
|
|
* Scale by half. This function uses externally-provided weights,
|
|
* multiplied by the following filter:
|
|
*
|
|
* 1/16 1/8 1/16
|
|
* 1/8 1/4 1/8
|
|
* 1/16 1/8 1/16
|
|
*
|
|
* Values are normalized so that the sum of the weights of contributing
|
|
* pixels is 1.
|
|
*/
|
|
image *scale_by_half(const image *weights, const char *name) const {
|
|
|
|
if (weights == NULL)
|
|
return scale_by_half(name);
|
|
|
|
ale_pos f = 0.5;
|
|
|
|
image *is = scale_generator(
|
|
(int) floor(height() * (double) f),
|
|
(int) floor(width() * (double) f), depth(), name);
|
|
|
|
assert(is);
|
|
|
|
for (unsigned int i = 0; i < is->height(); i++)
|
|
for (unsigned int j = 0; j < is->width(); j++) {
|
|
|
|
pixel value = pixel
|
|
|
|
( ( ((i > 0 && j > 0)
|
|
? (pixel) get_pixel(2 * i - 1, 2 * j - 1)
|
|
* (pixel) weights->get_pixel(2 * i - 1, 2 * j - 1)
|
|
* (ale_real) 0.0625
|
|
: pixel(0, 0, 0))
|
|
+ ((i > 0)
|
|
? (pixel) get_pixel(2 * i - 1, 2 * j)
|
|
* (pixel) weights->get_pixel(2 * i - 1, 2 * j)
|
|
* 0.125
|
|
: pixel(0, 0, 0))
|
|
+ ((i > 0 && j < is->width() - 1)
|
|
? (pixel) get_pixel(2 * i - 1, 2 * j + 1)
|
|
* (pixel) weights->get_pixel(2 * i - 1, 2 * j + 1)
|
|
* 0.0625
|
|
: pixel(0, 0, 0))
|
|
+ ((j > 0)
|
|
? (pixel) get_pixel(2 * i, 2 * j - 1)
|
|
* (pixel) weights->get_pixel(2 * i, 2 * j - 1)
|
|
* 0.125
|
|
: pixel(0, 0, 0))
|
|
+ get_pixel(2 * i, 2 * j)
|
|
* (pixel) weights->get_pixel(2 * i, 2 * j)
|
|
* 0.25
|
|
+ ((j < is->width() - 1)
|
|
? (pixel) get_pixel(2 * i, 2 * j + 1)
|
|
* (pixel) weights->get_pixel(2 * i, 2 * j + 1)
|
|
* 0.125
|
|
: pixel(0, 0, 0))
|
|
+ ((i < is->height() - 1 && j > 0)
|
|
? (pixel) get_pixel(2 * i + 1, 2 * j - 1)
|
|
* (pixel) weights->get_pixel(2 * i + 1, 2 * j - 1)
|
|
* 0.0625
|
|
: pixel(0, 0, 0))
|
|
+ ((i < is->height() - 1)
|
|
? (pixel) get_pixel(2 * i + 1, 2 * j)
|
|
* (pixel) weights->get_pixel(2 * i + 1, 2 * j)
|
|
* 0.125
|
|
: pixel(0, 0, 0))
|
|
+ ((i < is->height() && j < is->width() - 1)
|
|
? (pixel) get_pixel(2 * i + 1, 2 * j + 1)
|
|
* (pixel) weights->get_pixel(2 * i + 1, 2 * j + 1)
|
|
* 0.0625
|
|
: pixel(0, 0, 0)))
|
|
|
|
/
|
|
|
|
( ((i > 0 && j > 0)
|
|
? weights->get_pixel(2 * i - 1, 2 * j - 1)
|
|
* 0.0625
|
|
: pixel(0, 0, 0))
|
|
+ ((i > 0)
|
|
? weights->get_pixel(2 * i - 1, 2 * j)
|
|
* 0.125
|
|
: pixel(0, 0, 0))
|
|
+ ((i > 0 && j < is->width() - 1)
|
|
? weights->get_pixel(2 * i - 1, 2 * j + 1)
|
|
* 0.0625
|
|
: pixel(0, 0, 0))
|
|
+ ((j > 0)
|
|
? weights->get_pixel(2 * i, 2 * j - 1)
|
|
* 0.125
|
|
: pixel(0, 0, 0))
|
|
+ weights->get_pixel(2 * i, 2 * j)
|
|
* 0.25
|
|
+ ((j < is->width() - 1)
|
|
? weights->get_pixel(2 * i, 2 * j + 1)
|
|
* 0.125
|
|
: pixel(0, 0, 0))
|
|
+ ((i < is->height() - 1 && j > 0)
|
|
? weights->get_pixel(2 * i + 1, 2 * j - 1)
|
|
* 0.0625
|
|
: pixel(0, 0, 0))
|
|
+ ((i < is->height() - 1)
|
|
? weights->get_pixel(2 * i + 1, 2 * j)
|
|
* 0.125
|
|
: pixel(0, 0, 0))
|
|
+ ((i < is->height() && j < is->width() - 1)
|
|
? weights->get_pixel(2 * i + 1, 2 * j + 1)
|
|
* 0.0625
|
|
: pixel(0, 0, 0)) ) );
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
if (!finite(value[k]))
|
|
value[k] = 0;
|
|
|
|
is->set_pixel(i, j, value);
|
|
}
|
|
|
|
is->_offset = point(_offset[0] * f, _offset[1] * f);
|
|
|
|
return is;
|
|
}
|
|
|
|
/*
|
|
* 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. (Note that in the special case where weight is treated
|
|
* as certainty, using a geometric mean is probably correct.)
|
|
*
|
|
* We currently use a geometric mean to implement scaling of
|
|
* definition arrays.
|
|
*/
|
|
|
|
class defined_scale_by_half_threaded : public thread::decompose_domain {
|
|
image *is;
|
|
const image *iu;
|
|
protected:
|
|
void subdomain_algorithm(unsigned int thread,
|
|
int i_min, int i_max, int j_min, int j_max) {
|
|
|
|
#if 0
|
|
ale_real _0625 = (ale_real) 0.0625;
|
|
ale_real _125 = (ale_real) 0.125;
|
|
ale_real _25 = (ale_real) 0.25;
|
|
#endif
|
|
|
|
int ui_min = (int) i_min;
|
|
int ui_max = (int) i_max;
|
|
int uj_min = (int) j_min;
|
|
int uj_max = (int) j_max;
|
|
|
|
for (int i = ui_min; i < ui_max; i++)
|
|
for (int j = uj_min; j < uj_max; j++) {
|
|
|
|
#if 0
|
|
|
|
/*
|
|
* Calculate a geometric mean; this approach
|
|
* may be expensive on some platforms (e.g.,
|
|
* those without floating-point support in
|
|
* hardware).
|
|
*/
|
|
|
|
pixel value = pixel
|
|
|
|
( ( ((i > 0 && j > 0)
|
|
? ppow(iu->get_pixel(2 * i - 1, 2 * j - 1), _0625)
|
|
: pixel(0, 0, 0))
|
|
* ((i > 0)
|
|
? ppow(iu->get_pixel(2 * i - 1, 2 * j), _125)
|
|
: pixel(0, 0, 0))
|
|
* ((i > 0 && j < is->width() - 1)
|
|
? ppow(iu->get_pixel(2 * i - 1, 2 * j + 1), _0625)
|
|
: pixel(0, 0, 0))
|
|
* ((j > 0)
|
|
? ppow(iu->get_pixel(2 * i, 2 * j - 1), _125)
|
|
: pixel(0, 0, 0))
|
|
* ppow(iu->get_pixel(2 * i, 2 * j), _25)
|
|
* ((j < is->width() - 1)
|
|
? ppow(iu->get_pixel(2 * i, 2 * j + 1), _125)
|
|
: pixel(0, 0, 0))
|
|
* ((i < is->height() - 1 && j > 0)
|
|
? ppow(iu->get_pixel(2 * i + 1, 2 * j - 1), _0625)
|
|
: pixel(0, 0, 0))
|
|
* ((i < is->height() - 1)
|
|
? ppow(iu->get_pixel(2 * i + 1, 2 * j), _125)
|
|
: pixel(0, 0, 0))
|
|
* ((i < is->height() && j < is->width() - 1)
|
|
? ppow(iu->get_pixel(2 * i + 1, 2 * j + 1), _0625)
|
|
: pixel(0, 0, 0))));
|
|
#else
|
|
|
|
pixel value = iu->get_pixel(2 * i, 2 * j);
|
|
|
|
for (int ii = 2 * i - 1; ii <= 2 * i + 1; ii++)
|
|
for (int jj = 2 * j - 1; jj <= 2 * j + 1; jj++) {
|
|
if (ii < 0
|
|
|| jj < 0
|
|
|| ii > (int) iu->height() - 1
|
|
|| jj > (int) iu->height() - 1)
|
|
continue;
|
|
|
|
pixel value2 = iu->get_pixel(ii, jj);
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
if (value2[k] < value[k]
|
|
|| !finite(value2[k])) /* propagate non-finites */
|
|
value[k] = value2[k];
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
if (!finite(value[k]))
|
|
value[k] = 0;
|
|
|
|
is->set_pixel(i, j, value);
|
|
}
|
|
}
|
|
|
|
public:
|
|
defined_scale_by_half_threaded(image *_is, const image *_iu)
|
|
: decompose_domain(0, _is->height(),
|
|
0, _is->width()) {
|
|
is = _is;
|
|
iu = _iu;
|
|
}
|
|
};
|
|
|
|
image *defined_scale_by_half(const char *name) const {
|
|
ale_pos f = 0.5;
|
|
|
|
image *is = scale_generator(
|
|
(int) floor(height() * (double) f),
|
|
(int) floor(width() * (double) f), depth(), name);
|
|
|
|
assert(is);
|
|
|
|
defined_scale_by_half_threaded dsbht(is, this);
|
|
dsbht.run();
|
|
|
|
is->_offset = point(_offset[0] * f, _offset[1] * f);
|
|
|
|
return is;
|
|
}
|
|
|
|
/*
|
|
* Return an image scaled by some factor != 1.0, using bilinear
|
|
* interpolation.
|
|
*/
|
|
image *scale(ale_pos f, const char *name, int defined = 0) const {
|
|
|
|
/*
|
|
* We probably don't want to scale images by a factor of 1.0,
|
|
* or by non-positive values.
|
|
*/
|
|
assert (f != 1.0 && f > 0);
|
|
|
|
if (f > 1.0) {
|
|
image *is = scale_generator(
|
|
(int) floor(height() * (double) f),
|
|
(int) floor(width() * (double) f), depth(), name);
|
|
|
|
assert(is);
|
|
|
|
unsigned int i, j, k;
|
|
|
|
for (i = 0; i < is->height(); i++)
|
|
for (j = 0; j < is->width(); j++)
|
|
for (k = 0; k < is->depth(); k++)
|
|
is->set_pixel(i, j,
|
|
get_scaled_bl(point(i, j), f, defined));
|
|
|
|
is->_offset = point(_offset[0] * f, _offset[1] * f);
|
|
|
|
return is;
|
|
} else if (f == 0.5) {
|
|
if (defined == 0)
|
|
return scale_by_half(name);
|
|
else
|
|
return defined_scale_by_half(name);
|
|
} else {
|
|
image *is = scale(2*f, name, defined);
|
|
image *result = is->scale(0.5, name, defined);
|
|
delete is;
|
|
return result;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Extend the image area to the top, bottom, left, and right,
|
|
* initializing the new image areas with black pixels. Negative values
|
|
* shrink the image.
|
|
*/
|
|
virtual image *_extend(int top, int bottom, int left, int right) = 0;
|
|
|
|
static void extend(image **i, int top, int bottom, int left, int right) {
|
|
image *is = (*i)->_extend(top, bottom, left, right);
|
|
|
|
if (is != NULL) {
|
|
delete (*i);
|
|
*i = is;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clone
|
|
*/
|
|
image *clone(const char *name) const {
|
|
image *ic = scale_generator(
|
|
height(), width(), depth(), name);
|
|
|
|
assert(ic);
|
|
|
|
for (unsigned int i = 0; i < height(); i++)
|
|
for (unsigned int j = 0; j < width(); j++)
|
|
ic->set_pixel(i, j,
|
|
get_pixel(i, j));
|
|
|
|
|
|
ic->_offset = _offset;
|
|
|
|
ic->_apm_memo = _apm_memo;
|
|
ic->_acm_memo = _acm_memo;
|
|
ic->_accm_memo = _accm_memo;
|
|
ic->_apm = _apm;
|
|
ic->_acm = _acm;
|
|
ic->_accm = _accm;
|
|
|
|
return ic;
|
|
}
|
|
|
|
/*
|
|
* 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 {
|
|
|
|
/*
|
|
* This is a memoized function
|
|
*/
|
|
|
|
assert (k < _depth);
|
|
|
|
avg_channel_clamped_magnitude_memo();
|
|
return _accm[k];
|
|
}
|
|
|
|
pixel avg_channel_clamped_magnitude() const {
|
|
avg_channel_clamped_magnitude_memo();
|
|
return _accm;
|
|
}
|
|
|
|
/*
|
|
* Calculate the average (mean) magnitude of a channel across all
|
|
* pixels in an image.
|
|
*/
|
|
ale_real avg_channel_magnitude(unsigned int k) const {
|
|
|
|
/*
|
|
* This is a memoized function
|
|
*/
|
|
|
|
assert (k < _depth);
|
|
|
|
avg_channel_magnitude_memo();
|
|
return _acm[k];
|
|
}
|
|
|
|
pixel avg_channel_magnitude() const {
|
|
avg_channel_magnitude_memo();
|
|
return _acm;
|
|
}
|
|
|
|
/*
|
|
* 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 {
|
|
unsigned int i, j, k;
|
|
|
|
ale_accum accumulator;
|
|
ale_accum divisor = 0;
|
|
|
|
if (_apm_memo)
|
|
return _apm;
|
|
|
|
_apm_memo = 1;
|
|
accumulator = 0;
|
|
|
|
for (i = 0; i < _dimy; i++)
|
|
for (j = 0; j < _dimx; j++) {
|
|
pixel value = get_pixel(i, j);
|
|
|
|
for (k = 0; k < _depth; k++)
|
|
if (finite(value[k])) {
|
|
accumulator += value[k];
|
|
divisor += 1;
|
|
}
|
|
}
|
|
|
|
accumulator /= divisor;
|
|
|
|
_apm = accumulator;
|
|
|
|
return _apm;
|
|
}
|
|
|
|
virtual ~image() {
|
|
}
|
|
};
|
|
|
|
#endif
|