|
|
|
/* ============================================================
|
|
|
|
*
|
|
|
|
* 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
|