summaryrefslogtreecommitdiffstats
path: root/chalk/plugins/filters/cimg/kis_cimg_filter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chalk/plugins/filters/cimg/kis_cimg_filter.cpp')
-rw-r--r--chalk/plugins/filters/cimg/kis_cimg_filter.cpp711
1 files changed, 711 insertions, 0 deletions
diff --git a/chalk/plugins/filters/cimg/kis_cimg_filter.cpp b/chalk/plugins/filters/cimg/kis_cimg_filter.cpp
new file mode 100644
index 000000000..c8be0a6f6
--- /dev/null
+++ b/chalk/plugins/filters/cimg/kis_cimg_filter.cpp
@@ -0,0 +1,711 @@
+/*
+ * 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;
+ }
+}