Commit c63d1ffd authored by Gilles Caulier's avatar Gilles Caulier 🗼
Browse files

NoiseReduction Tool : add support of multicore CPU based on QtConcurrent API.

CCBUGS: 289204
parent 6e7f4a61
......@@ -20,6 +20,7 @@ Editor : OilPaint tool support multicore CPU.
Editor : Emboss tool support multicore CPU.
Editor : Raindrop tool support multicore CPU.
Editor : FilmGrain tool support multicore CPU.
Editor : NoiseReduction tool support multicore CPU.
Showfoto : Port of Thumbbar to Qt Model/view.
......
......@@ -33,7 +33,7 @@ filters
/filmgrain DONE DONE DONE DONE
enhance
/noisereductiontool DONE TODO DONE DONE
/noisereductiontool DONE DONE DONE DONE
/sharpentool DONE DONE DONE DONE
/restoration DONE TODO DONE DONE
/blurtool DONE DONE DONE DONE
......
......@@ -7,7 +7,7 @@
* Description : Wavelets Noise Reduction threaded image filter.
* This filter work in YCrCb color space.
*
* Copyright (C) 2005-2012 by Gilles Caulier <caulier dot gilles at gmail dot com>
* Copyright (C) 2005-2014 by Gilles Caulier <caulier dot gilles at gmail dot com>
* Copyright (C) 2008 by Marco Rossini <marco dot rossini at gmx dot net>
* Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
*
......@@ -30,6 +30,10 @@
#include <cmath>
// Qt includes
#include <QtConcurrentRun>
// Local includes
#include "dimg.h"
......@@ -279,17 +283,121 @@ void NRFilter::filterImage()
// -- Wavelets denoise methods -----------------------------------------------------------
void NRFilter::waveletDenoise(float* const fimg[3], unsigned int width, unsigned int height,
void NRFilter::calculteStdevMultithreaded(const Args& prm)
{
for (uint i = prm.start; runningFlag() && (i < prm.stop); ++i)
{
prm.fimg[*prm.hpass][i] -= prm.fimg[*prm.lpass][i];
if (prm.fimg[*prm.hpass][i] < *prm.thold && prm.fimg[*prm.hpass][i] > -*prm.thold)
{
if (prm.fimg[*prm.lpass][i] > 0.8)
{
prm.stdev[4] += prm.fimg[*prm.hpass][i] * prm.fimg[*prm.hpass][i];
prm.samples[4]++;
}
else if (prm.fimg[*prm.lpass][i] > 0.6)
{
prm.stdev[3] += prm.fimg[*prm.hpass][i] * prm.fimg[*prm.hpass][i];
prm.samples[3]++;
}
else if (prm.fimg[*prm.lpass][i] > 0.4)
{
prm.stdev[2] += prm.fimg[*prm.hpass][i] * prm.fimg[*prm.hpass][i];
prm.samples[2]++;
}
else if (prm.fimg[*prm.lpass][i] > 0.2)
{
prm.stdev[1] += prm.fimg[*prm.hpass][i] * prm.fimg[*prm.hpass][i];
prm.samples[1]++;
}
else
{
prm.stdev[0] += prm.fimg[*prm.hpass][i] * prm.fimg[*prm.hpass][i];
prm.samples[0]++;
}
}
}
}
void NRFilter::thresholdingMultithreaded(const Args& prm)
{
for (uint i = prm.start; runningFlag() && (i < prm.stop); ++i)
{
if (prm.fimg[*prm.lpass][i] > 0.8)
{
*prm.thold = prm.threshold * prm.stdev[4];
}
else if (prm.fimg[*prm.lpass][i] > 0.6)
{
*prm.thold = prm.threshold * prm.stdev[3];
}
else if (prm.fimg[*prm.lpass][i] > 0.4)
{
*prm.thold = prm.threshold * prm.stdev[2];
}
else if (prm.fimg[*prm.lpass][i] > 0.2)
{
*prm.thold = prm.threshold * prm.stdev[1];
}
else
{
*prm.thold = prm.threshold * prm.stdev[0];
}
if (prm.fimg[*prm.hpass][i] < -*prm.thold)
{
prm.fimg[*prm.hpass][i] += *prm.thold - *prm.thold * prm.softness;
}
else if (prm.fimg[*prm.hpass][i] > *prm.thold)
{
prm.fimg[*prm.hpass][i] -= *prm.thold - *prm.thold * prm.softness;
}
else
{
prm.fimg[*prm.hpass][i] *= prm.softness;
}
if (*prm.hpass)
{
prm.fimg[0][i] += prm.fimg[*prm.hpass][i];
}
}
}
void NRFilter::waveletDenoise(float* fimg[3], unsigned int width, unsigned int height,
float threshold, double softness)
{
float thold;
uint lpass = 0, hpass = 0, size;
double stdev[5];
unsigned int samples[5];
float thold;
uint lpass = 0, hpass = 0;
double stdev[5];
uint samples[5];
uint size = width * height;
size = width * height;
QScopedArrayPointer<float> temp(new float[qMax(width, height)]);
uint nbCore = QThreadPool::globalInstance()->maxThreadCount();
float step = size / nbCore;
uint vals[nbCore+1];
vals[0] = 0;
vals[nbCore] = size;
for (uint i = 1 ; i < nbCore ; ++i)
vals[i] = vals[i-1] + step;
QList <QFuture<void> > tasks;
Args prm;
prm.thold = &thold;
prm.lpass = &lpass;
prm.hpass = &hpass;
prm.threshold = threshold;
prm.softness = softness;
prm.stdev = &stdev[0];
prm.samples = &samples[0];
prm.fimg = fimg;
for (uint lev = 0; runningFlag() && (lev < 5); ++lev)
{
lpass = ((lev & 1) + 1);
......@@ -323,40 +431,19 @@ void NRFilter::waveletDenoise(float* const fimg[3], unsigned int width, unsigned
// calculate stdevs for all intensities
for (uint i = 0; runningFlag() && (i < size); ++i)
for (uint j = 0 ; runningFlag() && (j < nbCore) ; ++j)
{
fimg[hpass][i] -= fimg[lpass][i];
if (fimg[hpass][i] < thold && fimg[hpass][i] > -thold)
{
if (fimg[lpass][i] > 0.8)
{
stdev[4] += fimg[hpass][i] * fimg[hpass][i];
samples[4]++;
}
else if (fimg[lpass][i] > 0.6)
{
stdev[3] += fimg[hpass][i] * fimg[hpass][i];
samples[3]++;
}
else if (fimg[lpass][i] > 0.4)
{
stdev[2] += fimg[hpass][i] * fimg[hpass][i];
samples[2]++;
}
else if (fimg[lpass][i] > 0.2)
{
stdev[1] += fimg[hpass][i] * fimg[hpass][i];
samples[1]++;
}
else
{
stdev[0] += fimg[hpass][i] * fimg[hpass][i];
samples[0]++;
}
}
prm.start = vals[j];
prm.stop = vals[j+1];
tasks.append(QtConcurrent::run(this,
&NRFilter::calculteStdevMultithreaded,
prm
));
}
foreach(QFuture<void> t, tasks)
t.waitForFinished();
stdev[0] = sqrt(stdev[0] / (samples[0] + 1));
stdev[1] = sqrt(stdev[1] / (samples[1] + 1));
stdev[2] = sqrt(stdev[2] / (samples[2] + 1));
......@@ -365,48 +452,21 @@ void NRFilter::waveletDenoise(float* const fimg[3], unsigned int width, unsigned
// do thresholding
for (uint i = 0; runningFlag() && (i < size); ++i)
{
if (fimg[lpass][i] > 0.8)
{
thold = threshold * stdev[4];
}
else if (fimg[lpass][i] > 0.6)
{
thold = threshold * stdev[3];
}
else if (fimg[lpass][i] > 0.4)
{
thold = threshold * stdev[2];
}
else if (fimg[lpass][i] > 0.2)
{
thold = threshold * stdev[1];
}
else
{
thold = threshold * stdev[0];
}
if (fimg[hpass][i] < -thold)
{
fimg[hpass][i] += thold - thold * softness;
}
else if (fimg[hpass][i] > thold)
{
fimg[hpass][i] -= thold - thold * softness;
}
else
{
fimg[hpass][i] *= softness;
}
tasks.clear();
if (hpass)
{
fimg[0][i] += fimg[hpass][i];
}
for (uint j = 0 ; runningFlag() && (j < nbCore) ; ++j)
{
prm.start = vals[j];
prm.stop = vals[j+1];
tasks.append(QtConcurrent::run(this,
&NRFilter::thresholdingMultithreaded,
prm
));
}
foreach(QFuture<void> t, tasks)
t.waitForFinished();
hpass = lpass;
}
......@@ -468,175 +528,4 @@ void NRFilter::ycbcr2srgb(float** const fimg, int size)
}
}
// Methods not used.
/*
void NRFilter::srgb2xyz(float** const fimg, int size)
{
// fimg in [0:1], sRGB
float x, y, z;
for (int i = 0; i < size; ++i)
{
// scaling and gamma correction (approximate)
fimg[0][i] = pow(fimg[0][i], (float)2.2);
fimg[1][i] = pow(fimg[1][i], (float)2.2);
fimg[2][i] = pow(fimg[2][i], (float)2.2);
// matrix RGB -> XYZ, with D65 reference white (www.brucelindbloom.com)
x = 0.412424 * fimg[0][i] + 0.357579 * fimg[1][i] + 0.180464 * fimg[2][i];
y = 0.212656 * fimg[0][i] + 0.715158 * fimg[1][i] + 0.0721856 * fimg[2][i];
z = 0.0193324 * fimg[0][i] + 0.119193 * fimg[1][i] + 0.950444 * fimg[2][i];
// x = 0.412424 * fimg[0][i] + 0.212656 * fimg[1][i] + 0.0193324 * fimg[2][i];
// y = 0.357579 * fimg[0][i] + 0.715158 * fimg[1][i] + 0.119193 * fimg[2][i];
// z = 0.180464 * fimg[0][i] + 0.0721856 * fimg[1][i] + 0.950444 * fimg[2][i];
fimg[0][i] = x;
fimg[1][i] = y;
fimg[2][i] = z;
}
}
void NRFilter::xyz2srgb(float** const fimg, int size)
{
float r, g, b;
for (int i = 0; i < size; ++i)
{
// matrix RGB -> XYZ, with D65 reference white (www.brucelindbloom.com)
r = 3.24071 * fimg[0][i] - 1.53726 * fimg[1][i] - 0.498571 * fimg[2][i];
g = -0.969258 * fimg[0][i] + 1.87599 * fimg[1][i] + 0.0415557 * fimg[2][i];
b = 0.0556352 * fimg[0][i] - 0.203996 * fimg[1][i] + 1.05707 * fimg[2][i];
// r = 3.24071 * fimg[0][i] - 0.969258 * fimg[1][i]
// + 0.0556352 * fimg[2][i];
// g = -1.53726 * fimg[0][i] + 1.87599 * fimg[1][i]
// - 0.203996 * fimg[2][i];
// b = -0.498571 * fimg[0][i] + 0.0415557 * fimg[1][i]
// + 1.05707 * fimg[2][i];
// scaling and gamma correction (approximate)
r = r < 0 ? 0 : pow(r, (float)(1.0 / 2.2));
g = g < 0 ? 0 : pow(g, (float)(1.0 / 2.2));
b = b < 0 ? 0 : pow(b, (float)(1.0 / 2.2));
fimg[0][i] = r;
fimg[1][i] = g;
fimg[2][i] = b;
}
}
void NRFilter::lab2srgb(float** const fimg, int size)
{
float x, y, z;
for (int i = 0; i < size; ++i)
{
// convert back to normal LAB
fimg[0][i] = (fimg[0][i] - 0 * 16 * 27 / 24389.0) * 116;
fimg[1][i] = (fimg[1][i] - 0.5) * 500 * 2;
fimg[2][i] = (fimg[2][i] - 0.5) * 200 * 2.2;
// matrix
y = (fimg[0][i] + 16) / 116;
z = y - fimg[2][i] / 200.0;
x = fimg[1][i] / 500.0 + y;
// scale
if (x * x * x > 216 / 24389.0)
{
x = x * x * x;
}
else
{
x = (116 * x - 16) * 27 / 24389.0;
}
if (fimg[0][i] > 216 / 27.0)
{
y = y * y * y;
}
else
{
//y = fimg[0][i] * 27 / 24389.0;
y = (116 * y - 16) * 27 / 24389.0;
}
if (z * z * z > 216 / 24389.0)
{
z = z * z * z;
}
else
{
z = (116 * z - 16) * 27 / 24389.0;
}
// white reference
fimg[0][i] = x * 0.95047;
fimg[1][i] = y;
fimg[2][i] = z * 1.08883;
}
xyz2srgb(fimg, size);
}
void NRFilter::srgb2lab(float** const fimg, int size)
{
float l, a, b;
srgb2xyz(fimg, size);
for (int i = 0; i < size; ++i)
{
// reference white
fimg[0][i] /= 0.95047F;
//fimg[1][i] /= 1.00000; // (just for completeness)
fimg[2][i] /= 1.08883F;
// scale
if (fimg[0][i] > 216.0 / 24389.0)
{
fimg[0][i] = pow(fimg[0][i], (float)(1.0 / 3.0));
}
else
{
fimg[0][i] = (24389.0 * fimg[0][i] / 27.0 + 16.0) / 116.0;
}
if (fimg[1][i] > 216.0 / 24389.0)
{
fimg[1][i] = pow(fimg[1][i], (float)(1.0 / 3.0));
}
else
{
fimg[1][i] = (24389 * fimg[1][i] / 27.0 + 16.0) / 116.0;
}
if (fimg[2][i] > 216.0 / 24389.0)
{
fimg[2][i] = (float)pow(fimg[2][i], (float)(1.0 / 3.0));
}
else
{
fimg[2][i] = (24389.0 * fimg[2][i] / 27.0 + 16.0) / 116.0;
}
l = 116 * fimg[1][i] - 16;
a = 500 * (fimg[0][i] - fimg[1][i]);
b = 200 * (fimg[1][i] - fimg[2][i]);
fimg[0][i] = l / 116.0; // + 16 * 27 / 24389.0;
fimg[1][i] = a / 500.0 / 2.0 + 0.5;
fimg[2][i] = b / 200.0 / 2.2 + 0.5;
if (fimg[0][i] < 0)
{
fimg[0][i] = 0;
}
}
}
*/
} // namespace Digikam
......@@ -7,7 +7,7 @@
* Description : Wavelets Noise Reduction threaded image filter.
* This filter work in YCrCb color space.
*
* Copyright (C) 2005-2012 by Gilles Caulier <caulier dot gilles at gmail dot com>
* Copyright (C) 2005-2014 by Gilles Caulier <caulier dot gilles at gmail dot com>
* Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
*
* This program is free software; you can redistribute it
......@@ -81,23 +81,35 @@ public:
static void srgb2ycbcr(float** const fimg, int size);
private:
struct Args
{
uint start;
uint stop;
float* thold;
uint* lpass;
uint* hpass;
double* stdev;
uint* samples;
float** fimg;
float threshold;
double softness;
};
private:
void filterImage();
void waveletDenoise(float* const fimg[3], unsigned int width, unsigned int height,
void waveletDenoise(float* fimg[3], unsigned int width, unsigned int height,
float threshold, double softness);
inline void hatTransform(float* const temp, float* const base, int st, int size, int sc);
void ycbcr2srgb(float** const fimg, int size);
// Methods not used.
/*
void srgb2lab(float** const fimg, int size);
void lab2srgb(float** const fimg, int size);
void srgb2xyz(float** const fimg, int size);
void xyz2srgb(float** const fimg, int size);
*/
void calculteStdevMultithreaded(const Args& prm);
void thresholdingMultithreaded(const Args& prm);
private:
class Private;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment