You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/imageplugins/noisereduction/noisereduction.cpp

810 lines
26 KiB

/* ============================================================
*
* This file is a part of digiKam project
* http://www.digikam.org
*
* Date : 2005-05-25
* Description : Noise Reduction threaded image filter.
*
* Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
*
* Original Noise Filter algorithm copyright (C) 2005
* Peter Heckert <peter dot heckert at arcor dot de>
* from dcamnoise2 gimp plugin available at this url :
* http://home.arcor.de/peter.heckert/dcamnoise2-0.63.c
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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.
*
* ============================================================ */
#define IIR1(dest,src) (dest) = (d3 = ((((src) * b + d3) * b3 + d2) * b2 + d1) * b1)
#define IIR2(dest,src) (dest) = (d2 = ((((src) * b + d2) * b3 + d1) * b2 + d3) * b1)
#define IIR3(dest,src) (dest) = (d1 = ((((src) * b + d1) * b3 + d3) * b2 + d2) * b1)
#define IIR1A(dest,src) (dest) = fabs(d3 = ((((src) * b + d3) * b3 + d2) * b2 + d1) * b1)
#define IIR2A(dest,src) (dest) = fabs(d2 = ((((src) * b + d2) * b3 + d1) * b2 + d3) * b1)
#define IIR3A(dest,src) (dest) = fabs(d1 = ((((src) * b + d1) * b3 + d3) * b2 + d2) * b1)
#define FR 0.212671
#define FG 0.715160
#define FB 0.072169
// Local includes.
#include "ddebug.h"
#include "dimg.h"
#include "dimgimagefilters.h"
#include "noisereduction.h"
namespace DigikamNoiseReductionImagesPlugin
{
NoiseReduction::NoiseReduction(Digikam::DImg *orgImage, TQObject *parent,
double radius, double lsmooth, double effect, double texture, double sharp,
double csmooth, double lookahead, double gamma, double damping, double phase)
: Digikam::DImgThreadedFilter(orgImage, parent, "NoiseReduction")
{
m_radius = radius; /* default radius default = 1.0 */
m_sharp = sharp; /* Sharpness factor default = 0.25 */
m_lsmooth = lsmooth; /* Luminance Tolerance default = 1.0 */
m_effect = effect; /* Adaptive filter-effect threshold default = 0.08 */
m_texture = texture; /* Texture Detail default = 0.0 */
m_csmooth = csmooth; /* RGB Tolerance default = 1.0 */
m_lookahead = lookahead; /* Lookahead default = 2.0 */
m_gamma = gamma; /* Filter gamma default = 1.0 */
m_damping = damping; /* Phase jitter Damping default = 5.0 */
m_phase = phase; /* Area Noise Clip default = 1.0 */
m_iir.B = 0.0;
m_iir.b1 = 0.0;
m_iir.b2 = 0.0;
m_iir.b3 = 0.0;
m_iir.b0 = 0.0;
m_iir.r = 0.0;
m_iir.q = 0.0;
m_iir.p = 0;
m_clampMax = m_orgImage.sixteenBit() ? 65535 : 255;
initFilter();
}
// Remove noise on the region, given a source region, dest.
// region, width and height of the regions, and corner coordinates of
// a subregion to act upon. Everything outside the subregion is unaffected.
void NoiseReduction::filterImage(void)
{
int bytes = m_orgImage.bytesDepth(); // Bytes per pixel sample
uchar *srcPR = m_orgImage.bits();
uchar *destPR = m_destImage.bits();
int width = m_orgImage.width();
int height = m_orgImage.height();
int row, col, i, progress;
float prob = 0.0;
int w = (int)((m_radius + m_lookahead + m_damping + m_phase) * 4.0 + 40.0);
// NOTE: commented from original implementation
// if (radius < m_lookahead) w = m_lookahead * 4.0 + 40.0;
float csmooth = m_csmooth;
// Raw Filter preview
if (csmooth >= 0.99) csmooth = 1.0;
// Allocate and init buffers
uchar *src = new uchar[ TQMAX (width, height) * bytes ];
uchar *dest = new uchar[ TQMAX (width, height) * bytes ];
float *data = new float[ TQMAX (width, height) + 2*w ];
float *data2 = new float[ TQMAX (width, height) + 2*w ];
float *buffer = new float[ TQMAX (width, height) + 2*w ];
float *rbuf = new float[ TQMAX (width, height) + 2*w ];
float *tbuf = new float[ TQMAX (width, height) + 2*w ];
memset (src, 0, TQMAX (width, height) * bytes);
memset (dest, 0, TQMAX (width, height) * bytes);
for (i=0 ; i < TQMAX(width,height)+2*w-1 ; i++)
data[i] = data2[i] = buffer[i] = rbuf[i] = tbuf[i] = 0.0;
// Initialize the damping filter coefficients
iir_init(m_radius);
// blur the rows
for (row = 0 ; !m_cancel && (row < height) ; row++)
{
memcpy(src, srcPR + row*width*bytes, width*bytes);
memcpy(dest, src, width*bytes);
blur_line (data+w, data2+w, buffer+w, rbuf+w, tbuf+w, src, dest, width);
memcpy(destPR + row*width*bytes, dest, width*bytes);
progress = (int)(((double)row * 20.0) / height);
if ( progress%2 == 0 )
postProgress( progress );
}
// blur the cols
for (col = 0 ; !m_cancel && (col < width) ; col++)
{
for (int n = 0 ; n < height ; n++)
memcpy(src + n*bytes, destPR + (col + width*n)*bytes, bytes);
for (int n = 0 ; n < height ; n++)
memcpy(dest + n*bytes, srcPR + (col + width*n)*bytes, bytes);
blur_line (data+w, data2+w, buffer+w, rbuf+w, tbuf+w, src, dest, height);
for (int n = 0 ; n < height ; n++)
memcpy(destPR + (col + width*n)*bytes, dest + n*bytes, bytes);
progress = (int)(20.0 + ((double)col * 20.0) / width);
if ( progress%2 == 0 )
postProgress( progress );
}
// merge the source and destination (which currently contains
// the blurred version) images
for (row = 0 ; !m_cancel && (row < height) ; row++)
{
uchar *s = src;
uchar *d = dest;
unsigned short *s16 = (unsigned short *)src;
unsigned short *d16 = (unsigned short *)dest;
float value;
int u, v;
// get source row
memcpy(src, srcPR + row*width*bytes, width*bytes);
memcpy(dest, destPR + row*width*bytes, width*bytes);
// get dest row and combine the two
float t = m_csmooth;
float t2 = m_lsmooth;
// Values are squared, so that sliders get a nonlinear chracteristic
// for better adjustment accuracy when values are small.
t*=t;
t2*=t2;
for (u = 0 ; !m_cancel && (u < width) ; u++)
{
float dpix[3], spix[3];
float lum, red, green, blue;
float lum2, red2, green2, blue2;
if (m_orgImage.sixteenBit()) // 16 bits image
{
red = (float) s16[2]/(float)m_clampMax;
green = (float) s16[1]/(float)m_clampMax;
blue = (float) s16[0]/(float)m_clampMax;
}
else // 8 bits image
{
red = (float) s[2]/(float)m_clampMax;
green = (float) s[1]/(float)m_clampMax;
blue = (float) s[0]/(float)m_clampMax;
}
spix[2] = red;
spix[1] = green;
spix[0] = blue;
lum = (FR*red + FG*green + FB*blue);
if (m_orgImage.sixteenBit()) // 16 bits image
{
red2 = (float) d16[2]/(float)m_clampMax;
green2 = (float) d16[1]/(float)m_clampMax;
blue2 = (float) d16[0]/(float)m_clampMax;
}
else // 8 bits image
{
red2 = (float) d[2]/(float)m_clampMax;
green2 = (float) d[1]/(float)m_clampMax;
blue2 = (float) d[0]/(float)m_clampMax;
}
lum2 = (FR*red2 + FG*green2 + FB*blue2);
// Calculate luminance error (contrast error) for filtered template.
// This error is biggest, where edges are. Edges anyway cannot be filtered.
// Therefore we can correct luminance error in edges without increasing noise.
// Should be adjusted carefully, or not so carefully if you intentionally want to add noise.
// Noise, if not colorized, /can/ look good, so this makes sense.
float dl = lum - lum2;
// Multiply dl with first derivative of gamma curve divided by derivative value for midtone 0.5
// So bright tones will be corrected more (get more luminance noise and -information) than
// darker values because bright parts of image generally are less noisy, this is what we want.
dl *= pow(lum2/0.5, m_gamma-1.0);
if (t2 > 0.0)
dl *= (1.0 - exp(-dl*dl/(2.0*t2*t2)));
// NOTE: commented from original implementation
// if (dl > p) dl = p;
// if (dl < -p) dl = -p;
dpix[2] = red2 + dl;
dpix[1] = green2 + dl;
dpix[0] = blue2 + dl;
for (v = 0 ; !m_cancel && (v < 3) ; v++)
{
float value = spix[v];
float fvalue = dpix[v];
float mvalue = (value + fvalue)/2.0;
float diff = (value) - (fvalue);
// Multiply diff with first derivative of gamma curve divided by derivative value for midtone 0.5
// So midtones will stay unchanged, darker values get more blur and brighter values get less blur
// when we increase gamma.
diff *= pow(mvalue/0.5, m_gamma-1.0);
// Calculate noise probability for pixel
// TODO : probably it is not probability but an arbitrary curve.
// Probably we should provide a GUI-interface for this!!!
if (t > 0.0)
prob = exp(-diff*diff/(2.0*t*t));
else
prob = 0.0;
// Allow viewing of raw filter output
if (t >= 0.99)
prob = 1.0;
dpix[v] = value = fvalue * prob + value * (1.0 - prob);
}
if (m_orgImage.sixteenBit()) // 16 bits image
{
value = dpix[0]*(float)m_clampMax+0.5;
d16[0] = (unsigned short)CLAMP(value, 0, m_clampMax);
value = dpix[1]*(float)m_clampMax+0.5;
d16[1] = (unsigned short)CLAMP(value, 0, m_clampMax);
value = dpix[2]*(float)m_clampMax+0.5;
d16[2] = (unsigned short)CLAMP(value, 0, m_clampMax);
d16 += 4;
s16 += 4;
}
else // 8 bits image
{
value = dpix[0]*(float)m_clampMax+0.5;
d[0] = (uchar)CLAMP(value, 0, m_clampMax);
value = dpix[1]*(float)m_clampMax+0.5;
d[1] = (uchar)CLAMP(value, 0, m_clampMax);
value = dpix[2]*(float)m_clampMax+0.5;
d[2] = (uchar)CLAMP(value, 0, m_clampMax);
d += 4;
s += 4;
}
}
memcpy(destPR + row*width*bytes, dest, width*bytes);
progress = (int)(40.0 + ((double)row * 60.0) / height);
if ( progress%2 == 0 )
postProgress( progress );
}
delete [] data;
delete [] data2;
delete [] buffer;
delete [] rbuf;
delete [] tbuf;
delete [] dest;
delete [] src;
}
// This function is written as if it is blurring a column at a time,
// even though it can operate on rows, too. There is no difference
// in the processing of the lines, at least to the blur_line function.
// 'len' is the length of src and dest
void NoiseReduction::blur_line(float* const data, float* const data2, float* const buffer,
float* rbuf, float* tbuf, const uchar *src, uchar *dest, int len)
{
int b;
int row;
int idx;
unsigned short *src16 = (unsigned short *)src;
unsigned short *dest16 = (unsigned short *)dest;
// Calculate radius factors
for (row = 0, idx = 0 ; !m_cancel && (idx < len) ; row += 4, idx++)
{
// Color weigths are chosen proportional to Bayer Sensor pixel count
if (m_orgImage.sixteenBit()) // 16 bits image
{
data[idx] = (float) dest16[row+2] / (float)m_clampMax * 0.25; // Red color
data[idx] += (float) dest16[row+1] / (float)m_clampMax * 0.5; // Green color
data[idx] += (float) dest16[row] / (float)m_clampMax * 0.25; // Blue color
data[idx] = mypow(data[idx], m_gamma);
}
else // 8 bits image
{
data[idx] = (float) dest[row+2] / (float)m_clampMax * 0.25; // Red color
data[idx] += (float) dest[row+1] / (float)m_clampMax * 0.5; // Green color
data[idx] += (float) dest[row] / (float)m_clampMax * 0.25; // Blue color
data[idx] = mypow(data[idx], m_gamma);
}
}
filter(data, data2, buffer, rbuf, tbuf, len, -1);
// Do actual filtering
for (b = 0 ; !m_cancel && (b < 3) ; b++)
{
for (row = b, idx = 0 ; !m_cancel && (idx < len) ; row += 4, idx++)
{
if (m_orgImage.sixteenBit()) // 16 bits image
data[idx] = (float)src16[row] / (float)m_clampMax;
else // 8 bits image
data[idx] = (float)src[row] / (float)m_clampMax;
}
filter(data, data2, buffer, rbuf, tbuf, len, b);
for (row = b, idx = 0 ; !m_cancel && (idx < len) ; row += 4, idx++)
{
int value = (int)(data[idx] * (float)m_clampMax + 0.5);
if (m_orgImage.sixteenBit()) // 16 bits image
dest16[row] = (unsigned short)CLAMP( value, 0, m_clampMax);
else // 8 bits image
dest[row] = (uchar)CLAMP( value, 0, m_clampMax);
}
}
}
void NoiseReduction::iir_init(double r)
{
if (m_iir.r == r)
return;
// damping settings;
m_iir.r = r;
double q;
if ( r >= 2.5)
q = 0.98711 * r - 0.96330;
else
q = 3.97156 - 4.14554 * sqrt(1.0 - 0.26891 * r);
m_iir.q = q;
m_iir.b0 = 1.57825 + ((0.422205 * q + 1.4281) * q + 2.44413) * q;
m_iir.b1 = ((1.26661 * q +2.85619) * q + 2.44413) * q / m_iir.b0;
m_iir.b2 = - ((1.26661*q +1.4281) * q * q ) / m_iir.b0;
m_iir.b3 = 0.422205 * q * q * q / m_iir.b0;
m_iir.B = 1.0 - (m_iir.b1 + m_iir.b2 + m_iir.b3);
}
void NoiseReduction::box_filter(double *src, double *end, double *dest, double radius)
{
int boxwidth = 1;
float box = (*src);
float fbw = 2.0 * radius;
if (fbw < 1.0)
fbw = 1.0;
while(boxwidth+2 <= (int) fbw) boxwidth+=2, box += (src[boxwidth/2]) + (src[-boxwidth/2]);
double frac = (fbw - (double) boxwidth) / 2.0;
int bh = boxwidth / 2;
int bh1 = boxwidth / 2+1;
for ( ; src <= end ; src++, dest++)
{
*dest = (box + frac * ((src[bh1])+(src[-bh1]))) / fbw;
box = box - (src[-bh]) + (src[bh1]);
}
}
// Bidirectional IIR-filter, speed optimized
void NoiseReduction::iir_filter(float* const start, float* const end, float* dstart,
double radius, const int type)
{
if (!dstart)
dstart = start;
int width;
float *src = start;
float *dest = dstart;
float *dend = dstart + (end - start);
radius = floor((radius + 0.1) / 0.5) * 0.5;
// NOTE: commented from original implementation
// gfloat boxwidth = radius * 2.0;
// gint bw = (gint) boxwidth;
int ofs = (int)radius;
if (ofs < 1) ofs = 1;
double d1, d2, d3;
width = end - start + 1;
if (radius < 0.25)
{
if ( start != dest )
{
memcpy(dest, start, width*sizeof(*dest));
return;
}
}
iir_init(radius);
const double b1 = m_iir.b1;
const double b2 = m_iir.b2 / m_iir.b1;
const double b3 = m_iir.b3 / m_iir.b2;
const double b = m_iir.B / m_iir.b3;
switch(type)
{
case Gaussian:
d1 = d2 = d3 = *dest;
dend -= 6;
src--;
dest--;
while (dest < dend)
{
IIR1(*(++dest), *(++src));
IIR2(*(++dest), *(++src));
IIR3(*(++dest), *(++src));
IIR1(*(++dest), *(++src));
IIR2(*(++dest), *(++src));
IIR3(*(++dest), *(++src));
}
dend += 6;
while (1)
{
if (++dest > dend) break;
IIR1(*dest,*(++src));
if (++dest > dend) break;
IIR2(*dest,*(++src));
if (++dest > dend) break;
IIR3(*dest,*(++src));
}
d1 = d2 = d3 = dest[-1];
dstart += 6;
while (dest > dstart)
{
--dest, IIR1(*dest, *dest);
--dest, IIR2(*dest, *dest);
--dest, IIR3(*dest, *dest);
--dest, IIR1(*dest, *dest);
--dest, IIR2(*dest, *dest);
--dest, IIR3(*dest, *dest);
}
dstart -= 6;
while (1)
{
if (--dest < dstart) break;
IIR1(*dest, *dest);
if (--dest < dstart) break;
IIR2(*dest, *dest);
if (--dest < dstart) break;
IIR3(*dest, *dest);
}
break;
case SecondDerivative: // rectified and filtered second derivative, source and dest may be equal
d1 = d2 = d3 = 0.0;
dest[0] = dest[ofs] = 0.0;
dend -= 6;
dest--;
src--;
while (dest < dend)
{
++src, IIR1(*(++dest), src[ofs]-src[0]);
++src, IIR2(*(++dest), src[ofs]-src[0]);
++src, IIR3(*(++dest), src[ofs]-src[0]);
++src, IIR1(*(++dest), src[ofs]-src[0]);
++src, IIR2(*(++dest), src[ofs]-src[0]);
++src, IIR3(*(++dest), src[ofs]-src[0]);
}
dend += 6;
while (1)
{
if (++dest > dend) break;
++src, IIR1(*dest, src[ofs]-src[0]);
if (++dest > dend) break;
++src, IIR2(*dest, src[ofs]-src[0]);
if (++dest > dend) break;
++src, IIR3(*dest, src[ofs]-src[0]);
}
d1 = d2 = d3 = 0.0;
dest[-1] = dest[-ofs-1] = 0.0;
dstart += 6;
while (dest > dstart)
{
--dest, IIR1A(*dest, dest[0]-dest[-ofs]);
--dest, IIR2A(*dest, dest[0]-dest[-ofs]);
--dest, IIR3A(*dest, dest[0]-dest[-ofs]);
--dest, IIR1A(*dest, dest[0]-dest[-ofs]);
--dest, IIR2A(*dest, dest[0]-dest[-ofs]);
--dest, IIR3A(*dest, dest[0]-dest[-ofs]);
}
dstart -= 6;
while (1)
{
if (--dest < dstart) break;
IIR1A(*dest, dest[0]-dest[-ofs]);
if (--dest < dstart) break;
IIR2A(*dest, dest[0]-dest[-ofs]);
if (--dest < dstart) break;
IIR3A(*dest, dest[0]-dest[-ofs]);
}
break;
}
}
// A forward-backward box filter is used here and the radius is adapted to luminance jump.
// Radius is calculated fron 1st and 2nd derivative of intensity values.
// (Its not exactly 2nd derivative, but something similar, optimized by experiment)
// The radius variations are filtered. This reduces spatial phase jitter.
void NoiseReduction::filter(float *buffer, float *data, float *data2, float *rbuf,
float */*tbuf*/, int width, int color)
{
float *lp = data;
float *rp = data + width-1;
float *lp2 = data2;
float *blp = buffer;
float *brp = buffer + width-1;
float *rbuflp = rbuf;
float *rbufrp = rbuf + width-1;
float fboxwidth = m_radius*2.0;
float fradius = m_radius;
float *p1, *p2;
if (fboxwidth < 1.0) fboxwidth = 1.0 ;
if (fradius < 0.5) fradius = 0.5;
int i, pass;
int ofs, ofs2;
float maxrad;
float fbw;
float val;
double rfact = m_effect*m_effect;
double sharp = m_sharp;
ofs2 = (int)floor(m_damping * 2.0 + 0.1);
ofs = (int)floor(m_lookahead * 2.0 + 0.1);
int w = (int)(fboxwidth + m_damping + m_lookahead + m_phase + 2.0);
// Mirror image edges
for (i=1 ; i <= w ; i++)
blp[-i] = blp[i];
for (i=1 ; i <= w ; i++)
brp[i] = brp[-i];
if (color < 0) // Calc 2nd derivative
{
// boost high frequency in rbuf
for (p1 = blp, p2 = rbuflp ; p1 <= brp ; p1++, p2++)
{
*p2 = (sharp+1.0) * p1[0] - sharp * 0.5 * (p1[-ofs]+p1[ofs]);
}
iir_filter(rbuflp-w, rbufrp+w, blp-w, m_lookahead, SecondDerivative);
// Mirror image edges
for (i = 1 ; i <= w ; i++)
blp[-i] = blp[i];
for (i = 1 ; i <= w ; i++)
brp[i] = brp[-i];
// boost high frequency in rbuf
for (p1 = blp, p2 = rbuflp ; p1 <= brp ; p1++, p2++)
{
*p2 = ((sharp+1.0) * (p1[0]) - sharp * 0.5 * ((p1[-ofs2])+(p1[ofs2])));
}
// Mirror rbuf edges
for (i = 1 ; i <= w ; i++)
rbuflp[-i] = rbuflp[i];
for (i = 1 ; i <= w ; i++)
rbufrp[i] = rbufrp[-i];
// Lowpass (gauss) filter rbuf, remove phase jitter
iir_filter(rbuflp-w+5, rbufrp+w-5, rbuflp-w+5, m_damping, Gaussian);
for (i = -w+5; i < width-1+w-5 ; i++)
{
// NOTE: commented from original implementation
// val = rbuflp[i];
val = rbuflp[i]-rfact;
// Avoid division by zero, clip negative filter overshoot
if (val < rfact/fradius) val=rfact/fradius;
val = rfact/val;
// NOTE: commented from original implementation
// val = pow(val/fradius,m_phase)*fradius;
if (val < 0.5) val = 0.5;
rbuflp[i] = val*2.0;
}
// Mirror rbuf edges
for (i=1 ; i <= w ; i++)
rbuflp[-i] = rbuflp[i];
for (i=1 ; i <= w ; i++)
rbufrp[i] = rbufrp[-i];
return;
}
// Calc lowpass filtered input signal
iir_filter(blp-w+1, brp+w-1, lp2-w+1, m_radius, Gaussian);
// Subtract low frequency from input signal (aka original image data)
// and predistort this signal
val = m_texture + 1.0;
for (i = -w+1 ; i <= width-1+w-1 ; i++)
{
blp[i] = mypow(blp[i] - lp2[i], val);
}
float *src, *dest;
val = m_texture + 1.0;
pass = 2;
while (pass--)
{
float sum;
int ibw;
src = blp;
dest = lp;
maxrad = 0.0;
// Mirror left edge
for (i=1 ; i <= w ; i++)
src[-i] = src[i];
sum = (src[-1] += src[-2]);
// forward pass
for (rbuf = rbuflp-(int) m_phase ; rbuf <= rbufrp; src++, dest++, rbuf++)
{
// NOTE: commented from original implementation
//fbw = fabs( rbuf[-ofs2]*ll2+rbuf[-ofs2-1]*rl2);
fbw = *rbuf;
if (fbw > (maxrad += 1.0)) fbw = maxrad;
else if (fbw < maxrad) maxrad = fbw;
ibw = (int)fbw;
*src = sum += *src;
*dest = (sum-src[-ibw]+(src[-ibw]-src[-ibw-1])*(fbw-ibw))/fbw;
}
src = rp;
dest = brp;
maxrad = 0.0;
// Mirror right edge
for (i=1 ; i <= w ; i++)
src[i] = src[-i];
sum = (src[1] += src[2]);
// backward pass
for ( rbuf = rbufrp +(int) m_phase ; rbuf >= rbuflp; src--, dest--, rbuf--)
{
// NOTE: commented from original implementation
//fbw = fabs( rbuf[ofs2]*ll2+rbuf[ofs2+1]*rl2);
fbw = *rbuf;
if (fbw > (maxrad +=1.0)) fbw = maxrad;
else if (fbw < maxrad) maxrad = fbw;
ibw = (int)fbw;
*src = sum += *src;
*dest = (sum-src[ibw]+(src[ibw]-src[ibw+1])*(fbw-ibw))/fbw;
}
}
val = 1.0 / (m_texture + 1.0);
for (i = -w+1 ; i <= width-1+w-1 ; i++)
{
// Undo predistortion
blp[i]= mypow(blp[i],val);
// Add in low frequency
blp[i] += lp2[i];
// NOTE: commented from original implementation
// if (blp[i] >= 0.0) blp[i] = pow(blp[i],val);
// else blp[i] = 0.0;
}
}
} // NameSpace DigikamNoiseReductionImagesPlugin