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.
328 lines
12 KiB
328 lines
12 KiB
/* ============================================================
|
|
*
|
|
* This file is a part of digiKam project
|
|
* http://www.digikam.org
|
|
*
|
|
* Date : 2005-17-07
|
|
* Description : A Gaussian Blur threaded image filter.
|
|
*
|
|
* Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
*
|
|
* Original Gaussian Blur algorithm copyrighted 2004 by
|
|
* Pieter Z. Voloshyn <pieter_voloshyn at ame dot com dot br>.
|
|
*
|
|
* 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.
|
|
*
|
|
* ============================================================ */
|
|
|
|
// C++ includes.
|
|
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
|
|
// Local includes.
|
|
|
|
#include "ddebug.h"
|
|
#include "dimgimagefilters.h"
|
|
#include "dimggaussianblur.h"
|
|
|
|
namespace Digikam
|
|
{
|
|
|
|
DImgGaussianBlur::DImgGaussianBlur(DImg *orgImage, TQObject *parent, int radius)
|
|
: DImgThreadedFilter(orgImage, parent, "GaussianBlur")
|
|
{
|
|
m_radius = radius;
|
|
initFilter();
|
|
}
|
|
|
|
DImgGaussianBlur::DImgGaussianBlur(DImgThreadedFilter *parentFilter,
|
|
const DImg &orgImage, const DImg &destImage,
|
|
int progressBegin, int progressEnd, int radius)
|
|
: DImgThreadedFilter(parentFilter, orgImage, destImage, progressBegin, progressEnd,
|
|
parentFilter->filterName() + ": GaussianBlur")
|
|
{
|
|
m_radius = radius;
|
|
filterImage();
|
|
}
|
|
|
|
|
|
void DImgGaussianBlur::filterImage(void)
|
|
{
|
|
gaussianBlurImage(m_orgImage.bits(), m_orgImage.width(), m_orgImage.height(),
|
|
m_orgImage.sixteenBit(), m_radius);
|
|
}
|
|
|
|
/** Function to apply the Gaussian Blur on an image*/
|
|
|
|
void DImgGaussianBlur::gaussianBlurImage(uchar *data, int width, int height, bool sixteenBit, int radius)
|
|
{
|
|
if (!data || !width || !height)
|
|
{
|
|
DWarning() << ("DImgGaussianBlur::gaussianBlurImage: no image data available!")
|
|
<< endl;
|
|
return;
|
|
}
|
|
|
|
if (radius > 100) radius = 100;
|
|
if (radius <= 0)
|
|
{
|
|
m_destImage = m_orgImage;
|
|
return;
|
|
}
|
|
|
|
// Gaussian kernel computation using the Radius parameter.
|
|
|
|
int nKSize, nCenter;
|
|
double x, sd, factor, lnsd, lnfactor;
|
|
int i, j, n, h, w;
|
|
|
|
nKSize = 2 * radius + 1;
|
|
nCenter = nKSize / 2;
|
|
int *Kernel = new int[nKSize];
|
|
|
|
lnfactor = (4.2485 - 2.7081) / 10 * nKSize + 2.7081;
|
|
lnsd = (0.5878 + 0.5447) / 10 * nKSize - 0.5447;
|
|
factor = exp (lnfactor);
|
|
sd = exp (lnsd);
|
|
|
|
for (i = 0; !m_cancel && (i < nKSize); i++)
|
|
{
|
|
x = sqrt ((i - nCenter) * (i - nCenter));
|
|
Kernel[i] = (int)(factor * exp (-0.5 * pow ((x / sd), 2)) / (sd * sqrt (2.0 * M_PI)));
|
|
}
|
|
|
|
// Now, we need to convolve the image descriptor.
|
|
// I've worked hard here, but I think this is a very smart
|
|
// way to convolve an array, its very hard to explain how I reach
|
|
// this, but the trick here its to store the sum used by the
|
|
// previous pixel, so we sum with the other pixels that wasn't get.
|
|
|
|
int nSumA, nSumR, nSumG, nSumB, nCount, progress;
|
|
int nKernelWidth = radius * 2 + 1;
|
|
|
|
// We need to alloc a 2d array to help us to store the values
|
|
|
|
int** arrMult = Alloc2DArray (nKernelWidth, sixteenBit ? 65536 : 256);
|
|
|
|
for (i = 0; !m_cancel && (i < nKernelWidth); i++)
|
|
for (j = 0; !m_cancel && (j < (sixteenBit ? 65536 : 256)); j++)
|
|
arrMult[i][j] = j * Kernel[i];
|
|
|
|
// We need to copy our bits to blur bits
|
|
|
|
uchar* pOutBits = m_destImage.bits();
|
|
uchar* pBlur = new uchar[m_destImage.numBytes()];
|
|
|
|
memcpy (pBlur, data, m_destImage.numBytes());
|
|
|
|
// We need to initialize all the loop and iterator variables
|
|
|
|
nSumA = nSumR = nSumG = nSumB = nCount = i = j = 0;
|
|
unsigned short* data16 = (unsigned short*)data;
|
|
unsigned short* pBlur16 = (unsigned short*)pBlur;
|
|
unsigned short* pOutBits16 = (unsigned short*)pOutBits;
|
|
|
|
// Now, we enter in the main loop
|
|
|
|
for (h = 0; !m_cancel && (h < height); h++)
|
|
{
|
|
for (w = 0; !m_cancel && (w < width); w++, i+=4)
|
|
{
|
|
if (!sixteenBit) // 8 bits image.
|
|
{
|
|
uchar *org, *dst;
|
|
|
|
// first of all, we need to blur the horizontal lines
|
|
|
|
for (n = -radius; !m_cancel && (n <= radius); n++)
|
|
{
|
|
// if is inside...
|
|
if (IsInside (width, height, w + n, h))
|
|
{
|
|
// we points to the pixel
|
|
j = i + 4*n;
|
|
|
|
// finally, we sum the pixels using a method similar to assigntables
|
|
|
|
org = &data[j];
|
|
nSumA += arrMult[n + radius][org[3]];
|
|
nSumR += arrMult[n + radius][org[2]];
|
|
nSumG += arrMult[n + radius][org[1]];
|
|
nSumB += arrMult[n + radius][org[0]];
|
|
|
|
// we need to add to the counter, the kernel value
|
|
nCount += Kernel[n + radius];
|
|
}
|
|
}
|
|
|
|
if (nCount == 0) nCount = 1;
|
|
|
|
// now, we return to blur bits the horizontal blur values
|
|
dst = &pBlur[i];
|
|
dst[3] = (uchar)CLAMP (nSumA / nCount, 0, 255);
|
|
dst[2] = (uchar)CLAMP (nSumR / nCount, 0, 255);
|
|
dst[1] = (uchar)CLAMP (nSumG / nCount, 0, 255);
|
|
dst[0] = (uchar)CLAMP (nSumB / nCount, 0, 255);
|
|
|
|
// ok, now we reinitialize the variables
|
|
nSumA = nSumR = nSumG = nSumB = nCount = 0;
|
|
}
|
|
else // 16 bits image.
|
|
{
|
|
unsigned short *org, *dst;
|
|
|
|
// first of all, we need to blur the horizontal lines
|
|
|
|
for (n = -radius; !m_cancel && (n <= radius); n++)
|
|
{
|
|
// if is inside...
|
|
if (IsInside (width, height, w + n, h))
|
|
{
|
|
// we points to the pixel
|
|
j = i + 4*n;
|
|
|
|
// finally, we sum the pixels using a method similar to assigntables
|
|
|
|
org = &data16[j];
|
|
nSumA += arrMult[n + radius][org[3]];
|
|
nSumR += arrMult[n + radius][org[2]];
|
|
nSumG += arrMult[n + radius][org[1]];
|
|
nSumB += arrMult[n + radius][org[0]];
|
|
|
|
// we need to add to the counter, the kernel value
|
|
nCount += Kernel[n + radius];
|
|
}
|
|
}
|
|
|
|
if (nCount == 0) nCount = 1;
|
|
|
|
// now, we return to blur bits the horizontal blur values
|
|
dst = &pBlur16[i];
|
|
dst[3] = (unsigned short)CLAMP (nSumA / nCount, 0, 65535);
|
|
dst[2] = (unsigned short)CLAMP (nSumR / nCount, 0, 65535);
|
|
dst[1] = (unsigned short)CLAMP (nSumG / nCount, 0, 65535);
|
|
dst[0] = (unsigned short)CLAMP (nSumB / nCount, 0, 65535);
|
|
|
|
// ok, now we reinitialize the variables
|
|
nSumA = nSumR = nSumG = nSumB = nCount = 0;
|
|
}
|
|
}
|
|
|
|
progress = (int) (((double)h * 50.0) / height);
|
|
if ( progress%5 == 0 )
|
|
postProgress( progress );
|
|
}
|
|
|
|
// getting the blur bits, we initialize position variables
|
|
i = j = 0;
|
|
|
|
// We enter in the second main loop
|
|
for (w = 0; !m_cancel && (w < width); w++, i = w*4)
|
|
{
|
|
for (h = 0; !m_cancel && (h < height); h++, i += width*4)
|
|
{
|
|
if (!sixteenBit) // 8 bits image.
|
|
{
|
|
uchar *org, *dst;
|
|
|
|
// first of all, we need to blur the vertical lines
|
|
for (n = -radius; !m_cancel && (n <= radius); n++)
|
|
{
|
|
// if is inside...
|
|
if (IsInside(width, height, w, h + n))
|
|
{
|
|
// we points to the pixel
|
|
j = i + n * 4 * width;
|
|
|
|
// finally, we sum the pixels using a method similar to assigntables
|
|
org = &pBlur[j];
|
|
nSumA += arrMult[n + radius][org[3]];
|
|
nSumR += arrMult[n + radius][org[2]];
|
|
nSumG += arrMult[n + radius][org[1]];
|
|
nSumB += arrMult[n + radius][org[0]];
|
|
|
|
// we need to add to the counter, the kernel value
|
|
nCount += Kernel[n + radius];
|
|
}
|
|
}
|
|
|
|
if (nCount == 0) nCount = 1;
|
|
|
|
// To preserve Alpha channel.
|
|
memcpy (&pOutBits[i], &data[i], 4);
|
|
|
|
// now, we return to bits the vertical blur values
|
|
dst = &pOutBits[i];
|
|
dst[3] = (uchar)CLAMP (nSumA / nCount, 0, 255);
|
|
dst[2] = (uchar)CLAMP (nSumR / nCount, 0, 255);
|
|
dst[1] = (uchar)CLAMP (nSumG / nCount, 0, 255);
|
|
dst[0] = (uchar)CLAMP (nSumB / nCount, 0, 255);
|
|
|
|
// ok, now we reinitialize the variables
|
|
nSumA = nSumR = nSumG = nSumB = nCount = 0;
|
|
}
|
|
else // 16 bits image.
|
|
{
|
|
unsigned short *org, *dst;
|
|
|
|
// first of all, we need to blur the vertical lines
|
|
for (n = -radius; !m_cancel && (n <= radius); n++)
|
|
{
|
|
// if is inside...
|
|
if (IsInside(width, height, w, h + n))
|
|
{
|
|
// we points to the pixel
|
|
j = i + n * 4 * width;
|
|
|
|
// finally, we sum the pixels using a method similar to assigntables
|
|
org = &pBlur16[j];
|
|
nSumA += arrMult[n + radius][org[3]];
|
|
nSumR += arrMult[n + radius][org[2]];
|
|
nSumG += arrMult[n + radius][org[1]];
|
|
nSumB += arrMult[n + radius][org[0]];
|
|
|
|
// we need to add to the counter, the kernel value
|
|
nCount += Kernel[n + radius];
|
|
}
|
|
}
|
|
|
|
if (nCount == 0) nCount = 1;
|
|
|
|
// To preserve Alpha channel.
|
|
memcpy (&pOutBits16[i], &data16[i], 8);
|
|
|
|
// now, we return to bits the vertical blur values
|
|
dst = &pOutBits16[i];
|
|
dst[3] = (unsigned short)CLAMP (nSumA / nCount, 0, 65535);
|
|
dst[2] = (unsigned short)CLAMP (nSumR / nCount, 0, 65535);
|
|
dst[1] = (unsigned short)CLAMP (nSumG / nCount, 0, 65535);
|
|
dst[0] = (unsigned short)CLAMP (nSumB / nCount, 0, 65535);
|
|
|
|
// ok, now we reinitialize the variables
|
|
nSumA = nSumR = nSumG = nSumB = nCount = 0;
|
|
}
|
|
}
|
|
|
|
progress = (int) (50.0 + ((double)w * 50.0) / width);
|
|
if ( progress%5 == 0 )
|
|
postProgress( progress );
|
|
}
|
|
|
|
// now, we must free memory
|
|
Free2DArray (arrMult, nKernelWidth);
|
|
delete [] pBlur;
|
|
delete [] Kernel;
|
|
}
|
|
|
|
} // NameSpace Digikam
|