Denoise images with wavelets

I try to understand what following algorithm from this gimp plugin does to denoise an image:

/* Wavelet denoise GIMP plugin
*
* wavelet.c
* Copyright 2008 by Marco Rossini
*
* Implements the wavelet denoise code of UFRaw by Udi Fuchs
* which itself bases on the code by Dave Coffin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
*
* Instructions:
* compile with gimptool, eg. 'gimptool-2.0 --install wavelet-denoise.c'
*/

void
wavelet_denoise (float *fimg, unsigned int width,
unsigned int height, float threshold, double low, float a,
float b)
{
float *temp, thold;
unsigned int i, lev, lpass, hpass, size, col, row;
double stdev;
unsigned int samples;

size = width * height;

/* FIXME: replace by GIMP functions */
temp = (float *) malloc (MAX2 (width, height) * sizeof (float));

hpass = 0;
for (lev = 0; lev < 5; lev++)
{
if (b != 0)
gimp_progress_update (a + b * lev / 5.0);
lpass = ((lev & 1) + 1);
for (row = 0; row < height; row++)
{
hat_transform (temp, fimg[hpass] + row * width, 1, width, 1 << lev);
for (col = 0; col < width; col++)
{
fimg[lpass][row * width + col] = temp[col] * 0.25;
}
}
if (b != 0)
gimp_progress_update (a + b * (lev + 0.25) / 5.0);
for (col = 0; col < width; col++)
{
hat_transform (temp, fimg[lpass] + col, width, height, 1 << lev);
for (row = 0; row < height; row++)
{
fimg[lpass][row * width + col] = temp[row] * 0.25;
}
}
if (b != 0)
gimp_progress_update (a + b * (lev + 0.5) / 5.0);

thold =
5.0 / (1 << 6) * exp (-2.6 * sqrt (lev + 1)) * 0.8002 / exp (-2.6);

/* initialize stdev values for all intensities */
stdev = stdev = stdev = stdev = stdev = 0.0;
samples = samples = samples = samples = samples = 0;

/* calculate stdevs for all intensities */
for (i = 0; i < size; i++)
{
fimg[hpass][i] -= fimg[lpass][i];
if (fimg[hpass][i] < thold && fimg[hpass][i] > -thold)
{
if (fimg[lpass][i] > 0.8) {
stdev += fimg[hpass][i] * fimg[hpass][i];
samples++;
} else if (fimg[lpass][i] > 0.6) {
stdev += fimg[hpass][i] * fimg[hpass][i];
samples++;
} else if (fimg[lpass][i] > 0.4) {
stdev += fimg[hpass][i] * fimg[hpass][i];
samples++;
} else if (fimg[lpass][i] > 0.2) {
stdev += fimg[hpass][i] * fimg[hpass][i];
samples++;
} else {
stdev += fimg[hpass][i] * fimg[hpass][i];
samples++;
}
}
}
stdev = sqrt (stdev / (samples + 1));
stdev = sqrt (stdev / (samples + 1));
stdev = sqrt (stdev / (samples + 1));
stdev = sqrt (stdev / (samples + 1));
stdev = sqrt (stdev / (samples + 1));

if (b != 0)
gimp_progress_update (a + b * (lev + 0.75) / 5.0);

/* do thresholding */
for (i = 0; i < size; i++)
{
if (fimg[lpass][i] > 0.8) {
thold = threshold * stdev;
} else if (fimg[lpass][i] > 0.6) {
thold = threshold * stdev;
} else if (fimg[lpass][i] > 0.4) {
thold = threshold * stdev;
} else if (fimg[lpass][i] > 0.2) {
thold = threshold * stdev;
} else {
thold = threshold * stdev;
}

if (fimg[hpass][i] < -thold)
fimg[hpass][i] += thold - thold * low;
else if (fimg[hpass][i] > thold)
fimg[hpass][i] -= thold - thold * low;
else
fimg[hpass][i] *= low;

if (hpass)
fimg[i] += fimg[hpass][i];
}
hpass = lpass;
}

for (i = 0; i < size; i++)
fimg[i] = fimg[i] + fimg[lpass][i];

/* FIXME: replace by GIMP functions */
free (temp);
}

Code of UFRaw by Udi Fuchs, which itself bases on the code by Dave Coffin

Why are wavelets key to do that? How are the pixels calculated?

Any help would be greatly appreciated!

• Very interesting...
– Royi
Jan 18 '15 at 17:44
• @lennon310 Thanks for the edit :) Have added the denoising tag too.
– p2or
Jan 18 '15 at 19:57
• Looks like it is using wavelet thresholding to denoise the image. cs.haifa.ac.il/hagit/courses/seminars/wavelets/Presentations/… Jan 28 '15 at 20:01
• wavelet is to study nonstationary signals, for nonstationary signals, you often have nonstationary noise so yo can't just use a FIR filter. Wavelet thresholding method such as SURE estimates the noisy wavelet coefficients and eliminates them from the total wavelet coefficients, then transforms the signal back to time domain Jan 30 '15 at 2:50
• Thanks @IllegalImmigrant! Sounds like you are very familiar with it. :) Why don't you write an answer? Thanks again for sharing your knowledge.
– p2or
Feb 1 '15 at 14:41