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.
244 lines
7.2 KiB
244 lines
7.2 KiB
/* ============================================================
|
|
*
|
|
* This file is a part of digiKam project
|
|
* http://www.digikam.org
|
|
*
|
|
* Date : 2005-17-07
|
|
* Description : A Sharpen threaded image filter.
|
|
*
|
|
* Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
*
|
|
* Original Sharpen algorithm copyright 2002
|
|
* by Daniel M. Duley <mosfet@kde.org> from KImageEffect API.
|
|
*
|
|
* 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 SQ2PI 2.50662827463100024161235523934010416269302368164062
|
|
#define Epsilon 1.0e-12
|
|
|
|
// C++ includes.
|
|
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
|
|
// Local includes.
|
|
|
|
#include "ddebug.h"
|
|
#include "dimgimagefilters.h"
|
|
#include "dimgsharpen.h"
|
|
|
|
namespace Digikam
|
|
{
|
|
|
|
DImgSharpen::DImgSharpen(DImg *orgImage, TQObject *parent, double radius, double sigma)
|
|
: DImgThreadedFilter(orgImage, parent, "Sharpen")
|
|
{
|
|
m_radius = radius;
|
|
m_sigma = sigma;
|
|
initFilter();
|
|
}
|
|
|
|
DImgSharpen::DImgSharpen(DImgThreadedFilter *parentFilter,
|
|
const DImg &orgImage, const DImg &destImage,
|
|
int progressBegin, int progressEnd, double radius, double sigma)
|
|
: DImgThreadedFilter(parentFilter, orgImage, destImage, progressBegin, progressEnd,
|
|
parentFilter->filterName() + ": Sharpen")
|
|
{
|
|
m_radius = radius;
|
|
m_sigma = sigma;
|
|
// We need to provide support for orgImage == destImage.
|
|
// The algorithm does not support this out of the box, so use a temporary.
|
|
if (orgImage.bits() == destImage.bits())
|
|
m_destImage = DImg(destImage.width(), destImage.height(), destImage.sixteenBit());
|
|
filterImage();
|
|
if (orgImage.bits() == destImage.bits())
|
|
memcpy(destImage.bits(), m_destImage.bits(), m_destImage.numBytes());
|
|
}
|
|
|
|
void DImgSharpen::filterImage(void)
|
|
{
|
|
sharpenImage(m_radius, m_sigma);
|
|
}
|
|
|
|
/** Function to apply the sharpen filter on an image*/
|
|
|
|
void DImgSharpen::sharpenImage(double radius, double sigma)
|
|
{
|
|
if (m_orgImage.isNull())
|
|
{
|
|
DWarning() << k_funcinfo << "No image data available!"
|
|
<< endl;
|
|
return;
|
|
}
|
|
|
|
if (radius <= 0.0)
|
|
{
|
|
m_destImage = m_orgImage;
|
|
return;
|
|
}
|
|
|
|
double alpha, normalize=0.0;
|
|
long i=0, u, v;
|
|
|
|
int kernelWidth = getOptimalKernelWidth(radius, sigma);
|
|
|
|
if((int)m_orgImage.width() < kernelWidth)
|
|
{
|
|
DWarning() << k_funcinfo << "Image is smaller than radius!"
|
|
<< endl;
|
|
return;
|
|
}
|
|
|
|
double *kernel = new double[kernelWidth*kernelWidth];
|
|
|
|
if(!kernel)
|
|
{
|
|
DWarning() << k_funcinfo << "Unable to allocate memory!"
|
|
<< endl;
|
|
return;
|
|
}
|
|
|
|
for(v=(-kernelWidth/2) ; v <= (kernelWidth/2) ; v++)
|
|
{
|
|
for(u=(-kernelWidth/2) ; u <= (kernelWidth/2) ; u++)
|
|
{
|
|
alpha = exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
|
|
kernel[i] = alpha/(2.0*M_PI*sigma*sigma);
|
|
normalize += kernel[i];
|
|
i++;
|
|
}
|
|
}
|
|
|
|
kernel[i/2] = (-2.0)*normalize;
|
|
convolveImage(kernelWidth, kernel);
|
|
|
|
delete [] kernel;
|
|
}
|
|
|
|
bool DImgSharpen::convolveImage(const unsigned int order, const double *kernel)
|
|
{
|
|
uint x, y;
|
|
int mx, my, sx, sy, mcx, mcy, progress;
|
|
long kernelWidth, i;
|
|
double red, green, blue, alpha, normalize=0.0;
|
|
double *k=0;
|
|
DColor color;
|
|
|
|
kernelWidth = order;
|
|
|
|
if((kernelWidth % 2) == 0)
|
|
{
|
|
DWarning() << k_funcinfo << "Kernel width must be an odd number!"
|
|
<< endl;
|
|
return(false);
|
|
}
|
|
|
|
double *normal_kernel = new double[kernelWidth*kernelWidth];
|
|
|
|
if(!normal_kernel)
|
|
{
|
|
DWarning() << k_funcinfo << "Unable to allocate memory!"
|
|
<< endl;
|
|
return(false);
|
|
}
|
|
|
|
for(i=0 ; i < (kernelWidth*kernelWidth) ; i++)
|
|
normalize += kernel[i];
|
|
|
|
if(fabs(normalize) <= Epsilon)
|
|
normalize=1.0;
|
|
|
|
normalize = 1.0/normalize;
|
|
|
|
for(i=0 ; i < (kernelWidth*kernelWidth) ; i++)
|
|
normal_kernel[i] = normalize*kernel[i];
|
|
|
|
double maxClamp = m_destImage.sixteenBit() ? 16777215.0 : 65535.0;
|
|
|
|
for(y=0 ; !m_cancel && (y < m_destImage.height()) ; y++)
|
|
{
|
|
sy = y-(kernelWidth/2);
|
|
|
|
for(x=0 ; !m_cancel && (x < m_destImage.width()) ; x++)
|
|
{
|
|
k = normal_kernel;
|
|
red = green = blue = alpha = 0;
|
|
sy = y-(kernelWidth/2);
|
|
|
|
for(mcy=0 ; !m_cancel && (mcy < kernelWidth) ; mcy++, sy++)
|
|
{
|
|
my = sy < 0 ? 0 : sy > (int)m_destImage.height()-1 ? m_destImage.height()-1 : sy;
|
|
sx = x+(-kernelWidth/2);
|
|
|
|
for(mcx=0 ; !m_cancel && (mcx < kernelWidth) ; mcx++, sx++)
|
|
{
|
|
mx = sx < 0 ? 0 : sx > (int)m_destImage.width()-1 ? m_destImage.width()-1 : sx;
|
|
color = m_orgImage.getPixelColor(mx, my);
|
|
red += (*k)*(color.red() * 257.0);
|
|
green += (*k)*(color.green() * 257.0);
|
|
blue += (*k)*(color.blue() * 257.0);
|
|
alpha += (*k)*(color.alpha() * 257.0);
|
|
k++;
|
|
}
|
|
}
|
|
|
|
red = red < 0.0 ? 0.0 : red > maxClamp ? maxClamp : red+0.5;
|
|
green = green < 0.0 ? 0.0 : green > maxClamp ? maxClamp : green+0.5;
|
|
blue = blue < 0.0 ? 0.0 : blue > maxClamp ? maxClamp : blue+0.5;
|
|
alpha = alpha < 0.0 ? 0.0 : alpha > maxClamp ? maxClamp : alpha+0.5;
|
|
|
|
m_destImage.setPixelColor(x, y, DColor((int)(red / 257UL), (int)(green / 257UL),
|
|
(int)(blue / 257UL), (int)(alpha / 257UL),
|
|
m_destImage.sixteenBit()));
|
|
}
|
|
|
|
progress = (int)(((double)y * 100.0) / m_destImage.height());
|
|
if ( progress%5 == 0 )
|
|
postProgress( progress );
|
|
}
|
|
|
|
delete [] normal_kernel;
|
|
return(true);
|
|
}
|
|
|
|
int DImgSharpen::getOptimalKernelWidth(double radius, double sigma)
|
|
{
|
|
double normalize, value;
|
|
long kernelWidth;
|
|
long u;
|
|
|
|
if(radius > 0.0)
|
|
return((int)(2.0*ceil(radius)+1.0));
|
|
|
|
for(kernelWidth=5; ;)
|
|
{
|
|
normalize=0.0;
|
|
|
|
for(u=(-kernelWidth/2) ; u <= (kernelWidth/2) ; u++)
|
|
normalize += exp(-((double) u*u)/(2.0*sigma*sigma))/(SQ2PI*sigma);
|
|
|
|
u = kernelWidth/2;
|
|
value = exp(-((double) u*u)/(2.0*sigma*sigma))/(SQ2PI*sigma)/normalize;
|
|
|
|
if((long)(65535*value) <= 0)
|
|
break;
|
|
|
|
kernelWidth+=2;
|
|
}
|
|
|
|
return((int)kernelWidth-2);
|
|
}
|
|
|
|
} // NameSpace Digikam
|