219 lines
5.3 KiB
C++
219 lines
5.3 KiB
C++
// 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
|