|
|
|
/* ============================================================
|
|
|
|
*
|
|
|
|
* This file is a part of digiKam project
|
|
|
|
* http://www.digikam.org
|
|
|
|
*
|
|
|
|
* Date : 2005-03-27
|
|
|
|
* Description : Threaded image filter to fix hot pixels
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
|
|
* Copyright (C) 2005-2006 by Unai Garro <ugarro at users dot sourceforge dot net>
|
|
|
|
*
|
|
|
|
* 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>
|
|
|
|
|
|
|
|
// TQt includes.
|
|
|
|
|
|
|
|
#include <tqcolor.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqstringlist.h>
|
|
|
|
|
|
|
|
// Local includes.
|
|
|
|
|
|
|
|
#include "dimg.h"
|
|
|
|
#include "ddebug.h"
|
|
|
|
#include "hotpixelfixer.h"
|
|
|
|
|
|
|
|
#ifdef HAVE_FLOAT_H
|
|
|
|
#if HAVE_FLOAT_H
|
|
|
|
# include <float.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef DBL_MIN
|
|
|
|
# define DBL_MIN 1e-37
|
|
|
|
#endif
|
|
|
|
#ifndef DBL_MAX
|
|
|
|
# define DBL_MAX 1e37
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace DigikamHotPixelsImagesPlugin
|
|
|
|
{
|
|
|
|
|
|
|
|
HotPixelFixer::HotPixelFixer(Digikam::DImg *orgImage, TQObject *tqparent, const TQValueList<HotPixel>& hpList,
|
|
|
|
int interpolationMethod)
|
|
|
|
: Digikam::DImgThreadedFilter(orgImage, tqparent, "HotPixels")
|
|
|
|
{
|
|
|
|
m_hpList = hpList;
|
|
|
|
m_interpolationMethod = interpolationMethod;
|
|
|
|
mWeightList.clear();
|
|
|
|
|
|
|
|
initFilter();
|
|
|
|
}
|
|
|
|
|
|
|
|
HotPixelFixer::~HotPixelFixer()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void HotPixelFixer::filterImage(void)
|
|
|
|
{
|
|
|
|
TQValueList <HotPixel>::ConstIterator it;
|
|
|
|
TQValueList <HotPixel>::ConstIterator end(m_hpList.end());
|
|
|
|
for (it = m_hpList.begin() ; it != end ; ++it)
|
|
|
|
{
|
|
|
|
HotPixel hp = *it;
|
|
|
|
interpolate(m_orgImage, hp, m_interpolationMethod);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_destImage = m_orgImage;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Interpolates a pixel block
|
|
|
|
void HotPixelFixer::interpolate (Digikam::DImg &img, HotPixel &hp, int method)
|
|
|
|
{
|
|
|
|
const int xPos = hp.x();
|
|
|
|
const int yPos = hp.y();
|
|
|
|
bool sixtBits = img.sixteenBit();
|
|
|
|
|
|
|
|
// Interpolate pixel.
|
|
|
|
switch (method)
|
|
|
|
{
|
|
|
|
case AVERAGE_INTERPOLATION:
|
|
|
|
{
|
|
|
|
// We implement the bidimendional one first.
|
|
|
|
// TODO: implement the rest of directions (V & H) here
|
|
|
|
|
|
|
|
//case twodim:
|
|
|
|
// {
|
|
|
|
int sum_weight = 0;
|
|
|
|
double vr=0.0,vg=0.0,vb=0.0;
|
|
|
|
int x, y;
|
|
|
|
Digikam::DColor col;
|
|
|
|
|
|
|
|
for (x = xPos; x < xPos+hp.width(); ++x)
|
|
|
|
{
|
|
|
|
if (validPoint(img,TQPoint(x,yPos-1)))
|
|
|
|
{
|
|
|
|
col=img.getPixelColor(x,yPos-1);
|
|
|
|
vr += col.red();
|
|
|
|
vg += col.green();
|
|
|
|
vb += col.blue();
|
|
|
|
++sum_weight;
|
|
|
|
}
|
|
|
|
if (validPoint(img,TQPoint(x,yPos+hp.height())))
|
|
|
|
{
|
|
|
|
col=img.getPixelColor(x,yPos+hp.height());
|
|
|
|
vr += col.red();
|
|
|
|
vg += col.green();
|
|
|
|
vb += col.blue();
|
|
|
|
++sum_weight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (y = yPos; y < hp.height(); ++y)
|
|
|
|
{
|
|
|
|
if (validPoint(img,TQPoint(xPos-1,y)))
|
|
|
|
{
|
|
|
|
col=img.getPixelColor(xPos,y);
|
|
|
|
vr += col.red();
|
|
|
|
vg += col.green();
|
|
|
|
vb += col.blue();
|
|
|
|
++sum_weight;
|
|
|
|
}
|
|
|
|
if (validPoint(img,TQPoint(xPos+hp.width(),y)))
|
|
|
|
{
|
|
|
|
col=img.getPixelColor(xPos+hp.width(),y);
|
|
|
|
vr += col.red();
|
|
|
|
vg += col.green();
|
|
|
|
vb += col.blue();
|
|
|
|
++sum_weight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sum_weight > 0)
|
|
|
|
{
|
|
|
|
vr /= (double)sum_weight;
|
|
|
|
vg /= (double)sum_weight;
|
|
|
|
vb /= (double)sum_weight;
|
|
|
|
|
|
|
|
|
|
|
|
for (x = 0; x < hp.width(); ++x)
|
|
|
|
for (y = 0; y < hp.height(); ++y)
|
|
|
|
if (validPoint(img,TQPoint(xPos+x,yPos+y)))
|
|
|
|
{
|
|
|
|
int alpha=sixtBits ? 65535 : 255;
|
|
|
|
int ir=(int )round(vr),ig=(int) round(vg),ib=(int) round(vb);
|
|
|
|
img.setPixelColor(xPos+x,yPos+y,Digikam::DColor(ir,ig,ib,alpha,sixtBits));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} //Case average
|
|
|
|
|
|
|
|
case LINEAR_INTERPOLATION:
|
|
|
|
//(Bi)linear interpolation.
|
|
|
|
weightPixels (img,hp,LINEAR_INTERPOLATION,TWODIM_DIRECTION,sixtBits ? 65535: 255);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TQUADRATIC_INTERPOLATION:
|
|
|
|
// (Bi)quadratic interpolation.
|
|
|
|
weightPixels (img,hp,TQUADRATIC_INTERPOLATION,TWODIM_DIRECTION,sixtBits ? 65535 : 255);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CUBIC_INTERPOLATION:
|
|
|
|
// (Bi)cubic interpolation.
|
|
|
|
weightPixels (img,hp,CUBIC_INTERPOLATION,TWODIM_DIRECTION,sixtBits ? 65535 : 255);
|
|
|
|
break;
|
|
|
|
} //switch
|
|
|
|
}
|
|
|
|
|
|
|
|
void HotPixelFixer::weightPixels (Digikam::DImg &img, HotPixel &px, int method, Direction dir,int maxComponent)
|
|
|
|
{
|
|
|
|
//TODO: implement direction here too
|
|
|
|
|
|
|
|
for (int iComp = 0; iComp < 3; ++iComp)
|
|
|
|
{
|
|
|
|
// Obtain weight data block.
|
|
|
|
|
|
|
|
Weights w;
|
|
|
|
int polynomeOrder=-1;
|
|
|
|
|
|
|
|
switch (method)
|
|
|
|
{
|
|
|
|
case AVERAGE_INTERPOLATION: // Gilles: to prevent warnings from compiler.
|
|
|
|
break;
|
|
|
|
case LINEAR_INTERPOLATION:
|
|
|
|
polynomeOrder=1;
|
|
|
|
break;
|
|
|
|
case TQUADRATIC_INTERPOLATION:
|
|
|
|
polynomeOrder=2;
|
|
|
|
break;
|
|
|
|
case CUBIC_INTERPOLATION:
|
|
|
|
polynomeOrder=3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (polynomeOrder<0) return;
|
|
|
|
|
|
|
|
// In the one-dimensional case, the width must be 1,
|
|
|
|
// and the size must be stored in height
|
|
|
|
|
|
|
|
w.setWidth(dir == TWODIM_DIRECTION ? px.width() : 1);
|
|
|
|
w.setHeight(dir == HORIZONTAL_DIRECTION ? px.width() : px.height());
|
|
|
|
w.setPolynomeOrder(polynomeOrder);
|
|
|
|
w.setTwoDim(dir == TWODIM_DIRECTION);
|
|
|
|
|
|
|
|
//TODO: check this, it must not recalculate existing calculated weights
|
|
|
|
//for now I don't think it is finding the duplicates fine, so it uses
|
|
|
|
//the previous one always...
|
|
|
|
|
|
|
|
//if (mWeightList.tqfind(w)==mWeightList.end())
|
|
|
|
//{
|
|
|
|
w.calculateWeights();
|
|
|
|
|
|
|
|
// mWeightList.append(w);
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
// Calculate weighted pixel sum.
|
|
|
|
for (int y = 0; y<px.height(); ++y)
|
|
|
|
{
|
|
|
|
for (int x = 0; x < px.width(); ++x)
|
|
|
|
{
|
|
|
|
if (validPoint (img,TQPoint(px.x()+x,px.y()+y)))
|
|
|
|
{
|
|
|
|
double sum_weight = 0.0, v = 0.0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < w.positions().count(); ++i)
|
|
|
|
{
|
|
|
|
// In the one-dimensional case, only the y coordinate is used.
|
|
|
|
const int xx = px.x()+(dir == VERTICAL_DIRECTION ? x :
|
|
|
|
dir== HORIZONTAL_DIRECTION ? w.positions()[i].y() : w.positions()[i].x());
|
|
|
|
const int yy = px.y()+(dir == HORIZONTAL_DIRECTION ? y :
|
|
|
|
w.positions()[i].y());
|
|
|
|
|
|
|
|
if (validPoint (img,TQPoint(xx, yy)))
|
|
|
|
{
|
|
|
|
//TODO: check this. I think it is broken
|
|
|
|
double weight;
|
|
|
|
if (dir==VERTICAL_DIRECTION)
|
|
|
|
{
|
|
|
|
weight = w[i][y][0];
|
|
|
|
}
|
|
|
|
else if (dir==HORIZONTAL_DIRECTION)
|
|
|
|
{
|
|
|
|
weight=w[i][0][x];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
weight=w[i][y][x];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iComp==0) v += weight * img.getPixelColor(xx, yy).red();
|
|
|
|
else if (iComp==1) v += weight * img.getPixelColor(xx, yy).green();
|
|
|
|
else v += weight * img.getPixelColor(xx, yy).blue();
|
|
|
|
|
|
|
|
sum_weight += weight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Digikam::DColor color=img.getPixelColor(px.x()+x,px.y()+y);
|
|
|
|
int component;
|
|
|
|
if (fabs (v) <= DBL_MIN)
|
|
|
|
component=0;
|
|
|
|
else if (sum_weight >= DBL_MIN)
|
|
|
|
{
|
|
|
|
component=(int) (v/sum_weight);
|
|
|
|
//Clamp value
|
|
|
|
if (component<0) component=0;
|
|
|
|
if (component>maxComponent) component=maxComponent;
|
|
|
|
}
|
|
|
|
else if (v >= 0.0)
|
|
|
|
component=maxComponent;
|
|
|
|
else
|
|
|
|
component=0;
|
|
|
|
|
|
|
|
if (iComp==0) color.setRed(component);
|
|
|
|
else if (iComp==1) color.setGreen(component);
|
|
|
|
else color.setBlue(component);
|
|
|
|
|
|
|
|
|
|
|
|
img.setPixelColor(px.x()+x,px.y()+y,color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // NameSpace DigikamHotPixelsImagesPlugin
|