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.
712 lines
22 KiB
712 lines
22 KiB
/*
|
|
* This file is part of Chalk
|
|
*
|
|
* Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
|
|
*
|
|
* 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 of the License, 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Ported from the CImg Gimp plugin by Victor Stinner and uses CImg by David Tschumperlé.
|
|
* See: http://www.girouette-stinner.com/castor/gimp.html?girouette=ad761bc2f4dcfda1cb44c587da17f86c
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <vector>
|
|
|
|
#include <tqpoint.h>
|
|
#include <tqspinbox.h>
|
|
|
|
#include <tdelocale.h>
|
|
#include <kiconloader.h>
|
|
#include <kinstance.h>
|
|
#include <tdemessagebox.h>
|
|
#include <kstandarddirs.h>
|
|
#include <tdetempfile.h>
|
|
#include <kdebug.h>
|
|
#include <kgenericfactory.h>
|
|
#include <knuminput.h>
|
|
|
|
#include <kis_colorspace_factory_registry.h>
|
|
#include <kis_doc.h>
|
|
#include <kis_filter_registry.h>
|
|
#include <kis_image.h>
|
|
#include <kis_iterators_pixel.h>
|
|
#include <kis_layer.h>
|
|
#include <kis_meta_registry.h>
|
|
#include <kis_painter.h>
|
|
#include <kis_global.h>
|
|
#include <kis_types.h>
|
|
|
|
#include "kis_cimgconfig_widget.h"
|
|
#include "kis_cimg_filter.h"
|
|
|
|
#include "CImg.h"
|
|
|
|
using namespace cimg_library;
|
|
typedef unsigned char uchar;
|
|
|
|
|
|
KisCImgFilterConfiguration::KisCImgFilterConfiguration()
|
|
: KisFilterConfiguration("cimg", 1)
|
|
{
|
|
nb_iter = 1;
|
|
dt = 20.0;
|
|
sigma = 1.4;
|
|
dlength = 0.8;
|
|
dtheta = 45.0;
|
|
onormalize = false;
|
|
power1 = 0.1;
|
|
power2 = 0.9;
|
|
gauss_prec = 3.0;
|
|
linear = true;
|
|
}
|
|
|
|
void KisCImgFilterConfiguration::fromXML(const TQString & s)
|
|
{
|
|
KisFilterConfiguration::fromXML( s );
|
|
|
|
nb_iter = getInt("nb_iter", 1);
|
|
dt = getDouble("dt", 20.0);
|
|
sigma = getDouble("sigma", 1.4);
|
|
dlength = getDouble("dlength", 0.8);
|
|
dtheta = getDouble("dtheta", 45.0);
|
|
onormalize = getBool("onormalize", false);
|
|
power1 = getDouble("power1", 0.1);
|
|
power2 = getDouble("power2", 0.9);
|
|
gauss_prec = getDouble("gauss_pref", 3.0);
|
|
linear = getBool("linear", true);
|
|
}
|
|
|
|
|
|
TQString KisCImgFilterConfiguration::toString()
|
|
{
|
|
m_properties.clear();
|
|
|
|
setProperty("nb_iter", nb_iter);
|
|
setProperty("dt", dt);
|
|
setProperty("sigma", sigma);
|
|
setProperty("dlength", dlength);
|
|
setProperty("dtheta", dtheta);
|
|
setProperty("onormalize", onormalize);
|
|
setProperty("power1", power1);
|
|
setProperty("power2", power2);
|
|
setProperty("gauss_prec", gauss_prec);
|
|
setProperty("linear", linear);
|
|
|
|
return KisFilterConfiguration::toString();
|
|
}
|
|
|
|
KisCImgFilter::KisCImgFilter()
|
|
: KisFilter(id(), "enhance", i18n("&CImg Image Restoration...")),
|
|
eigen(CImg<>(2,1), CImg<>(2,2))
|
|
{
|
|
restore = true;
|
|
inpaint = false;
|
|
resize = false;
|
|
visuflow = NULL;
|
|
|
|
/* restore */
|
|
nb_iter = 1;
|
|
dt = 20.0f;
|
|
sigma = 0.8f;
|
|
dlength = 0.8;
|
|
dtheta = 45.0;
|
|
onormalize = false;
|
|
power1 = 0.5;
|
|
power2 = 0.9;
|
|
|
|
/* inpainting *
|
|
nb_iter = 100;
|
|
dt = 50.0f;
|
|
sigma = 2.0;
|
|
power1 = 0.1;
|
|
power2 = 100;
|
|
dlength = 0.8;
|
|
dtheta = 45.0;
|
|
*/
|
|
|
|
/* resize *
|
|
nb_iter = 1;
|
|
dt = 30.0f;
|
|
sigma = 2.0;
|
|
dlength = 0.8;
|
|
dtheta = 45.0;
|
|
power1 = 0.01;
|
|
power2 = 100.0;
|
|
*/
|
|
|
|
/* visualflow *
|
|
nb_iter = 1;
|
|
dt = 30.0f;
|
|
dlength = 0.5;
|
|
dtheta = 20.0;
|
|
onormalize = false;
|
|
*/
|
|
|
|
gauss_prec = 3.0f;
|
|
linear = true;
|
|
}
|
|
|
|
|
|
void KisCImgFilter::process(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisFilterConfiguration* configuration, const TQRect& rect)
|
|
{
|
|
Q_UNUSED(dst);
|
|
|
|
TQ_INT32 width = rect.width();
|
|
TQ_INT32 height = rect.height();
|
|
|
|
// Copy the src data into a CImg type image with three channels and no alpha.
|
|
// XXX: This means that a CImg is always rgba; find the quickest way to get 8-bit rgb from any colorspace & find a way
|
|
// to warn in the gui of loss of precision. XXX: Add this to the ColorSpaceAPI doc.
|
|
|
|
img = CImg<>(width, height, 1, 3);
|
|
|
|
KisColorSpace * cs = src->colorSpace();
|
|
KisColorSpace* rgb16CS = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA16"),"");
|
|
KisPaintDeviceSP srcRGB16;
|
|
if(rgb16CS)
|
|
{
|
|
srcRGB16 = new KisPaintDevice(*src.data());
|
|
srcRGB16->convertTo(rgb16CS);
|
|
KisRectIteratorPixel it = srcRGB16->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), false);
|
|
while (!it.isDone()) {
|
|
TQ_UINT16* data = reinterpret_cast<TQ_UINT16*>(it.rawData());
|
|
|
|
TQ_INT32 x = it.x() - rect.x();
|
|
TQ_INT32 y = it.y() - rect.y();
|
|
|
|
img(x, y, 0) = data[0];
|
|
img(x, y, 1) = data[1];
|
|
img(x, y, 2) = data[2];
|
|
|
|
++it;
|
|
}
|
|
} else {
|
|
kdDebug() << "The RGB16 colorspace is not available, will work in 8bit." << endl;
|
|
KisRectIteratorPixel it = src->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), false);
|
|
while (!it.isDone()) {
|
|
|
|
TQColor color;
|
|
cs->toTQColor(it.rawData(), &color);
|
|
|
|
TQ_INT32 x = it.x() - rect.x();
|
|
TQ_INT32 y = it.y() - rect.y();
|
|
|
|
img(x, y, 0) = color.red();
|
|
img(x, y, 1) = color.green();
|
|
img(x, y, 2) = color.blue();
|
|
|
|
++it;
|
|
}
|
|
}
|
|
|
|
// Copy the config data into local variables for easy cut & pasting from the original plugin
|
|
|
|
KisCImgFilterConfiguration * cfg = (KisCImgFilterConfiguration*)configuration;
|
|
|
|
nb_iter = cfg->nb_iter;
|
|
dt = cfg->dt;
|
|
dlength = cfg->dlength;
|
|
dtheta = cfg->dtheta;
|
|
sigma = cfg->sigma;
|
|
power1 = cfg->power1;
|
|
power2 = cfg->power2;
|
|
gauss_prec = cfg->gauss_prec;
|
|
onormalize = cfg->onormalize;
|
|
linear = cfg->linear;
|
|
|
|
if (process() && !cancelRequested()) {
|
|
|
|
|
|
if(rgb16CS)
|
|
{
|
|
{
|
|
KisRectIteratorPixel it = srcRGB16->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), true);
|
|
while (!it.isDone()) {
|
|
TQ_INT32 x = it.x() - rect.x();
|
|
TQ_INT32 y = it.y() - rect.y();
|
|
|
|
TQ_UINT16* data = reinterpret_cast<TQ_UINT16*>(it.rawData());
|
|
|
|
data[0] = img(x, y, 0) ;
|
|
data[1] = img(x, y, 1) ;
|
|
data[2] = img(x, y, 2) ;
|
|
|
|
++it;
|
|
}
|
|
}
|
|
srcRGB16->convertTo(cs);
|
|
KisPainter p(dst);
|
|
p.bitBlt(rect.x(), rect.y(), COMPOSITE_OVER, srcRGB16, rect.x(), rect.y(), rect.width(), rect.height() );
|
|
} else {
|
|
KisRectIteratorPixel it = dst->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), true);
|
|
|
|
while (!it.isDone()) {
|
|
|
|
if (it.isSelected()) {
|
|
|
|
TQ_INT32 x = it.x() - rect.x();
|
|
TQ_INT32 y = it.y() - rect.y();
|
|
|
|
TQColor color((int)img(x, y, 0), (int)img(x, y, 1), (int)img(x, y, 2));
|
|
|
|
cs->fromTQColor(color, it.rawData());
|
|
}
|
|
|
|
++it;
|
|
}
|
|
}
|
|
} else {
|
|
// Everything went wrong; notify user and restore old state
|
|
}
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Cut & Pasted code starts here....
|
|
//----------------------------------------------------------------------------
|
|
|
|
void get_geom(const char *geom, int &geom_w, int &geom_h)
|
|
{
|
|
char tmp[16];
|
|
std::sscanf(geom,"%d%7[^0-9]%d%7[^0-9]",&geom_w,tmp,&geom_h,tmp+1);
|
|
if (tmp[0]=='%') geom_w=-geom_w;
|
|
if (tmp[1]=='%') geom_h=-geom_h;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void KisCImgFilter::cleanup()
|
|
{
|
|
img0 = flow = G = dest = sum= W = CImg<>();
|
|
mask = CImg<uchar> ();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool KisCImgFilter::prepare()
|
|
{
|
|
if (!restore && !inpaint && !resize && !visuflow)
|
|
{
|
|
// XXX: Do KDE messagebox
|
|
// g_message ("You must specify one of the restore, inpaint, resize or flow mode !");
|
|
return false;
|
|
}
|
|
|
|
// Init algorithm parameters
|
|
//---------------------------
|
|
if (restore) if (!prepare_restore()) return false;
|
|
if (inpaint) if (!prepare_inpaint()) return false;
|
|
if (resize) if (!prepare_resize()) return false;
|
|
if (visuflow) if (!prepare_visuflow()) return false;
|
|
|
|
if (!check_args()) return false;
|
|
|
|
// Init images
|
|
//------------
|
|
dest = CImg<>(img.width,img.height,1,img.dim);
|
|
sum = CImg<>(img.width,img.height,1);
|
|
W = CImg<>(img.width,img.height,1,2);
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool KisCImgFilter::check_args()
|
|
{
|
|
if (power2 < power1)
|
|
{
|
|
// XXX: Do KDE messagebox
|
|
// g_message ("Error : p2<p1 !");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool KisCImgFilter::prepare_restore()
|
|
{
|
|
CImgStats stats(img, false);
|
|
img.normalize((float)stats.min, (float)stats.max);
|
|
img0 = img;
|
|
G = CImg<>(img.width,img.height,1,3);
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool KisCImgFilter::prepare_inpaint()
|
|
{
|
|
const char *file_m = NULL; //cimg_option("-m",(const char*)NULL,"Input inpainting mask");
|
|
if (!file_m)
|
|
{
|
|
// XXX: Do KDE messagebox
|
|
// g_message ("You need to specify an inpainting mask (option '-m') !");
|
|
return false;
|
|
}
|
|
|
|
const unsigned int dilate = 0; //cimg_option("-dilate",0,"Inpainting mask dilatation");
|
|
const unsigned int ip_init = 3; //cimg_option("-init",3,"Inpainting init (0=black, 1=white, 2=noise, 3=unchanged, 4=interpol)");
|
|
if (cimg::strncasecmp("block",file_m,5))
|
|
mask = CImg<uchar>(file_m);
|
|
else {
|
|
int l=16; std::sscanf(file_m,"block%d",&l);
|
|
mask = CImg<uchar>(img.width/l,img.height/l);
|
|
cimg_mapXY(mask,x,y) mask(x,y)=(x+y)%2;
|
|
}
|
|
mask.resize(img.width,img.height,1,1);
|
|
if (dilate) mask.dilate(dilate);
|
|
switch (ip_init) {
|
|
case 0 : { cimg_mapXYV(img,x,y,k) if (mask(x,y)) img(x,y,k) = 0; } break;
|
|
case 1 : { cimg_mapXYV(img,x,y,k) if (mask(x,y)) img(x,y,k) = 255; } break;
|
|
case 2 : { cimg_mapXYV(img,x,y,k) if (mask(x,y)) img(x,y,k) = (float)(255*cimg::rand()); } break;
|
|
case 3 : break;
|
|
case 4 : {
|
|
CImg<uchar> tmask(mask),ntmask(tmask);
|
|
CImg_3x3(M,uchar);
|
|
CImg_3x3(I,float);
|
|
while (CImgStats(ntmask,false).max>0) {
|
|
cimg_map3x3(tmask,x,y,0,0,M) if (Mcc && (!Mpc || !Mnc || !Mcp || !Mcn)) {
|
|
const float ccp = Mcp?0.0f:1.0f, cpc = Mpc?0.0f:1.0f,
|
|
cnc = Mnc?0.0f:1.0f, ccn = Mcn?0.0f:1.0f, csum = ccp + cpc + cnc + ccn;
|
|
cimg_mapV(img,k) {
|
|
cimg_get3x3(img,x,y,0,k,I);
|
|
img(x,y,k) = (ccp*Icp + cpc*Ipc + cnc*Inc + ccn*Icn)/csum;
|
|
}
|
|
ntmask(x,y) = 0;
|
|
}
|
|
tmask = ntmask;
|
|
}
|
|
} break;
|
|
default: break;
|
|
}
|
|
img0=img;
|
|
G = CImg<>(img.width,img.height,1,3,0);
|
|
CImg_3x3(g,uchar);
|
|
CImg_3x3(I,float);
|
|
cimg_map3x3(mask,x,y,0,0,g) if (!gcc && !(gnc-gcc) && !(gcc-gpc) && !(gcn-gcc) && !(gcc-gcp)) cimg_mapV(img,k) {
|
|
cimg_get3x3(img,x,y,0,k,I);
|
|
const float ix = 0.5f*(Inc-Ipc), iy = 0.5f*(Icn-Icp);
|
|
G(x,y,0)+= ix*ix; G(x,y,1)+= ix*iy; G(x,y,2)+= iy*iy;
|
|
}
|
|
G.blur(sigma);
|
|
{ cimg_mapXY(G,x,y)
|
|
{
|
|
G.get_tensor(x,y).symeigen(eigen(0),eigen(1));
|
|
const float
|
|
l1 = eigen(0)[0],
|
|
l2 = eigen(0)[1],
|
|
u = eigen(1)[0],
|
|
v = eigen(1)[1],
|
|
ng = (float)std::sqrt(l1+l2),
|
|
n1 = (float)(1.0/std::pow(1+ng,power1)),
|
|
n2 = (float)(1.0/std::pow(1+ng,power2)),
|
|
sr1 = (float)std::sqrt(n1),
|
|
sr2 = (float)std::sqrt(n2);
|
|
G(x,y,0) = sr1*u*u + sr2*v*v;
|
|
G(x,y,1) = u*v*(sr1-sr2);
|
|
G(x,y,2) = sr1*v*v + sr2*u*u;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool KisCImgFilter::prepare_resize()
|
|
{
|
|
const char *geom = NULL; //cimg_option("-g",(const char*)NULL,"Output image geometry");
|
|
const bool anchor = true; //cimg_option("-anchor",true,"Anchor original pixels");
|
|
if (!geom) throw CImgArgumentException("You need to specify an output geomety (option -g)");
|
|
int w,h; get_geom(geom,w,h);
|
|
mask = CImg<uchar>(img.width,img.height,1,1,255);
|
|
if (!anchor) mask.resize(w,h,1,1,1); else mask = ~mask.resize(w,h,1,1,4);
|
|
img0 = img.get_resize(w,h,1,-100,1);
|
|
img.resize(w,h,1,-100,3);
|
|
G = CImg<>(img.width,img.height,1,3);
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool KisCImgFilter::prepare_visuflow()
|
|
{
|
|
const char *geom = "100%x100%"; //cimg_option("-g","100%x100%","Output geometry");
|
|
//const char *file_i = (const char *)NULL; //cimg_option("-i",(const char*)NULL,"Input init image");
|
|
const bool normalize = false; //cimg_option("-norm",false,"Normalize input flow");
|
|
|
|
int w,h; get_geom(geom,w,h);
|
|
if (!cimg::strcasecmp(visuflow,"circle")) { // Create a circular vector flow
|
|
flow = CImg<>(400,400,1,2);
|
|
cimg_mapXY(flow,x,y) {
|
|
const float ang = (float)(std::atan2(y-0.5*flow.dimy(),x-0.5*flow.dimx()));
|
|
flow(x,y,0) = -(float)std::sin(ang);
|
|
flow(x,y,1) = (float)std::cos(ang);
|
|
}
|
|
}
|
|
if (!cimg::strcasecmp(visuflow,"radial")) { // Create a radial vector flow
|
|
flow = CImg<>(400,400,1,2);
|
|
cimg_mapXY(flow,x,y) {
|
|
const float ang = (float)(std::atan2(y-0.5*flow.dimy(),x-0.5*flow.dimx()));
|
|
flow(x,y,0) = (float)std::cos(ang);
|
|
flow(x,y,1) = (float)std::sin(ang);
|
|
}
|
|
}
|
|
if (!flow.data) flow = CImg<>(visuflow);
|
|
flow.resize(w,h,1,2,3);
|
|
if (normalize) flow.orientation_pointwise();
|
|
/* if (file_i) img = CImg<>(file_i);
|
|
else img = CImg<>(flow.width,flow.height,1,1,0).noise(100,2); */
|
|
img0=img;
|
|
img0.fill(0);
|
|
float color[3]={255,255,255};
|
|
img0.draw_quiver(flow,color,15,-10);
|
|
G = CImg<>(img.width,img.height,1,3);
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void KisCImgFilter::compute_smoothed_tensor()
|
|
{
|
|
if (visuflow || inpaint) return;
|
|
CImg_3x3(I,float);
|
|
G.fill(0);
|
|
cimg_mapV(img,k) cimg_map3x3(img,x,y,0,k,I) {
|
|
const float ix = 0.5f*(Inc-Ipc), iy = 0.5f*(Icn-Icp);
|
|
G(x,y,0)+= ix*ix; G(x,y,1)+= ix*iy; G(x,y,2)+= iy*iy;
|
|
}
|
|
G.blur(sigma);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void KisCImgFilter::compute_normalized_tensor()
|
|
{
|
|
if (restore || resize) cimg_mapXY(G,x,y) {
|
|
G.get_tensor(x,y).symeigen(eigen(0),eigen(1));
|
|
const float
|
|
l1 = eigen(0)[0],
|
|
l2 = eigen(0)[1],
|
|
u = eigen(1)[0],
|
|
v = eigen(1)[1],
|
|
n1 = (float)(1.0/std::pow(1.0f+l1+l2,0.5f*power1)),
|
|
n2 = (float)(1.0/std::pow(1.0f+l1+l2,0.5f*power2));
|
|
G(x,y,0) = n1*u*u + n2*v*v;
|
|
G(x,y,1) = u*v*(n1-n2);
|
|
G(x,y,2) = n1*v*v + n2*u*u;
|
|
}
|
|
if (visuflow) cimg_mapXY(G,x,y) {
|
|
const float
|
|
u = flow(x,y,0),
|
|
v = flow(x,y,1),
|
|
n = (float)std::pow(u*u+v*v,0.25f),
|
|
nn = n < 1e-5 ? 1 : n;
|
|
G(x,y,0) = u*u/nn;
|
|
G(x,y,1) = u*v/nn;
|
|
G(x,y,2) = v*v/nn;
|
|
}
|
|
|
|
const CImgStats stats(G,false);
|
|
G /= cimg::max(std::fabs(stats.max), std::fabs(stats.min));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void KisCImgFilter::compute_W(float cost, float sint)
|
|
{
|
|
cimg_mapXY(W,x,y) {
|
|
const float
|
|
a = G(x,y,0),
|
|
b = G(x,y,1),
|
|
c = G(x,y,2),
|
|
u = a*cost + b*sint,
|
|
v = b*cost + c*sint;
|
|
W(x,y,0) = u;
|
|
W(x,y,1) = v;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void KisCImgFilter::compute_LIC_back_forward(int x, int y)
|
|
{
|
|
float l, X,Y, cu, cv, lsum=0;
|
|
const float
|
|
fsigma2 = 2*dt*(W(x,y,0)*W(x,y,0) + W(x,y,1)*W(x,y,1)),
|
|
length = gauss_prec*(float)std::sqrt(fsigma2);
|
|
|
|
if (linear) {
|
|
|
|
// Integrate with linear interpolation
|
|
cu = W(x,y,0); cv = W(x,y,1); X=(float)x; Y=(float)y;
|
|
for (l=0; l<length && X>=0 && Y>=0 && X<=W.dimx()-1 && Y<=W.dimy()-1; l+=dlength) {
|
|
float u = (float)(W.linear_pix2d(X,Y,0)), v = (float)(W.linear_pix2d(X,Y,1));
|
|
const float coef = (float)std::exp(-l*l/fsigma2);
|
|
if ((cu*u+cv*v)<0) { u=-u; v=-v; }
|
|
cimg_mapV(dest,k) dest(x,y,k)+=(float)(coef*img.linear_pix2d(X,Y,k));
|
|
X+=dlength*u; Y+=dlength*v; cu=u; cv=v; lsum+=coef;
|
|
}
|
|
cu = W(x,y,0); cv = W(x,y,1); X=x-dlength*cu; Y=y-dlength*cv;
|
|
for (l=dlength; l<length && X>=0 && Y>=0 && X<=W.dimx()-1 && Y<=W.dimy()-1; l+=dlength) {
|
|
float u = (float)(W.linear_pix2d(X,Y,0)), v = (float)(W.linear_pix2d(X,Y,1));
|
|
const float coef = (float)std::exp(-l*l/fsigma2);
|
|
if ((cu*u+cv*v)<0) { u=-u; v=-v; }
|
|
cimg_mapV(dest,k) dest(x,y,k)+=(float)(coef*img.linear_pix2d(X,Y,k));
|
|
X-=dlength*u; Y-=dlength*v; cu=u; cv=v; lsum+=coef;
|
|
}
|
|
} else {
|
|
|
|
// Integrate with non linear interpolation
|
|
cu = W(x,y,0); cv = W(x,y,1); X=(float)x; Y=(float)y;
|
|
for (l=0; l<length && X>=0 && Y>=0 && X<=W.dimx()-1 && Y<=W.dimy()-1; l+=dlength) {
|
|
float u = W((int)X,(int)Y,0), v = W((int)X,(int)Y,1);
|
|
const float coef = (float)std::exp(-l*l/fsigma2);
|
|
if ((cu*u+cv*v)<0) { u=-u; v=-v; }
|
|
cimg_mapV(dest,k) dest(x,y,k)+=(float)(coef*img.linear_pix2d(X,Y,k));
|
|
X+=dlength*u; Y+=dlength*v; cu=u; cv=v; lsum+=coef;
|
|
}
|
|
cu = W(x,y,0); cv = W(x,y,1); X=x-dlength*cu; Y=y-dlength*cv;
|
|
for (l=dlength; l<length && X>=0 && Y>=0 && X<=W.dimx()-1 && Y<=W.dimy()-1; l+=dlength) {
|
|
float u = W((int)X,(int)Y,0), v = W((int)X,(int)Y,1);
|
|
const float coef = (float)std::exp(-l*l/fsigma2);
|
|
if ((cu*u+cv*v)<0) { u=-u; v=-v; }
|
|
cimg_mapV(dest,k) dest(x,y,k)+=(float)(coef*img.linear_pix2d(X,Y,k));
|
|
X-=dlength*u; Y-=dlength*v; cu=u; cv=v; lsum+=coef;
|
|
}
|
|
}
|
|
sum(x,y)+=lsum;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void KisCImgFilter::compute_LIC(int &progressSteps)
|
|
{
|
|
dest.fill(0);
|
|
sum.fill(0);
|
|
for (float theta=(180%(int)dtheta)/2.0f; theta<180; theta+=dtheta)
|
|
{
|
|
const float
|
|
rad = (float)(theta*cimg::PI/180.0),
|
|
cost = (float)std::cos(rad),
|
|
sint = (float)std::sin(rad);
|
|
|
|
// Compute vector field w = sqrt(T)*a_alpha
|
|
compute_W(cost, sint);
|
|
|
|
// Compute the LIC along w in backward and forward directions
|
|
cimg_mapXY(dest,x,y)
|
|
{
|
|
setProgress(progressSteps);
|
|
progressSteps++;
|
|
|
|
if (cancelRequested()) {
|
|
return;
|
|
}
|
|
|
|
if (!mask.data || mask(x,y)) compute_LIC_back_forward(x,y);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void KisCImgFilter::compute_average_LIC()
|
|
{
|
|
cimg_mapXY(dest,x,y)
|
|
{
|
|
if (sum(x,y)>0)
|
|
cimg_mapV(dest,k) dest(x,y,k) /= sum(x,y);
|
|
else
|
|
cimg_mapV(dest,k) dest(x,y,k) = img(x,y,k);
|
|
}
|
|
}
|
|
|
|
|
|
bool KisCImgFilter::process()
|
|
{
|
|
if (!prepare()) return false;
|
|
|
|
setProgressTotalSteps(dest.width * dest.height * nb_iter * (int)ceil(180 / dtheta));
|
|
setProgressStage(i18n("Applying image restoration filter..."), 0);
|
|
|
|
//-------------------------------------
|
|
// Begin regularization PDE iterations
|
|
//-------------------------------------
|
|
int progressSteps = 0;
|
|
for (unsigned int iter=0; iter<nb_iter; iter++)
|
|
{
|
|
// Compute smoothed structure tensor field G
|
|
compute_smoothed_tensor();
|
|
|
|
// Compute normalized tensor field sqrt(T) in G
|
|
compute_normalized_tensor();
|
|
|
|
// Compute LIC's along different angle projections a_\alpha
|
|
compute_LIC(progressSteps);
|
|
|
|
if (cancelRequested()) {
|
|
break;
|
|
}
|
|
|
|
// Average all the LIC's
|
|
compute_average_LIC();
|
|
|
|
// Next step
|
|
img = dest;
|
|
}
|
|
|
|
setProgressDone();
|
|
// Save result and end program
|
|
//-----------------------------
|
|
if (visuflow) dest.mul(flow.get_norm_pointwise()).normalize(0,255);
|
|
if (onormalize) dest.normalize(0,255);
|
|
cleanup();
|
|
return true;
|
|
}
|
|
|
|
KisFilterConfigWidget * KisCImgFilter::createConfigurationWidget(TQWidget* parent, KisPaintDeviceSP /*dev*/)
|
|
{
|
|
return new KisCImgconfigWidget(this, parent);
|
|
}
|
|
|
|
KisFilterConfiguration* KisCImgFilter::configuration(TQWidget* nwidget)
|
|
{
|
|
KisCImgconfigWidget * widget = (KisCImgconfigWidget *) nwidget;
|
|
if( widget == 0 )
|
|
{
|
|
KisCImgFilterConfiguration * cfg = new KisCImgFilterConfiguration();
|
|
TQ_CHECK_PTR(cfg);
|
|
return cfg;
|
|
|
|
} else {
|
|
return widget->config();
|
|
}
|
|
}
|
|
|
|
ColorSpaceIndependence KisCImgFilter::colorSpaceIndependence()
|
|
{
|
|
KisColorSpace* rgb16CS = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA16"),"");
|
|
if(rgb16CS)
|
|
{
|
|
return TO_RGBA16;
|
|
} else {
|
|
return TO_RGBA8;
|
|
}
|
|
}
|