249 lines
6.7 KiB
C++
249 lines
6.7 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_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
|