/* Function to apply the waves effect * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Amplitude => Sinoidal maximum height. * Frequency => Frequency value. * FillSides => Like a boolean variable. * Direction => Vertical or horizontal flag. * * Theory => This is an amazing effect, very funny, and very simple to * understand. You just need understand how qSin and qCos works. */ void DistortionFXFilter::waves(DImg* orgImage, DImg* destImage, int Amplitude, int Frequency, bool FillSides, bool Direction) { if (Amplitude < 0) { Amplitude = 0; } if (Frequency < 0) { Frequency = 0; } Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.Amplitude = Amplitude; prm.Frequency = Frequency; prm.FillSides = FillSides; if (Direction) // Horizontal { QList<int> vals = multithreadedSteps(orgImage->height()); QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::wavesHorizontalMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); } else { QList<int> vals = multithreadedSteps(orgImage->width()); QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::wavesVerticalMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); } }
void RefocusFilter::convolveImage(const Args& prm) { int progress; QList<int> vals = multithreadedSteps(prm.width); for (int y1 = 0; runningFlag() && (y1 < prm.height); ++y1) { QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { tasks.append(QtConcurrent::run(this, &RefocusFilter::convolveImageMultithreaded, vals[j], vals[j+1], y1, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); // Update the progress bar in dialog. progress = (int)(((double)y1 * 100.0) / prm.height); if (progress % 5 == 0) { postProgress(progress); } } }
/** This method have been implemented following this report in bugzilla : https://bugs.kde.org/show_bug.cgi?id=148540 We use YCbCr color space to perform noise addition. Please follow this url for details about this color space : http://en.allexperts.com/e/y/yc/ycbcr.htm */ void FilmGrainFilter::filterImage() { if (d->settings.lumaIntensity <= 0 || d->settings.chromaBlueIntensity <= 0 || d->settings.chromaRedIntensity <= 0 || !d->settings.isDirty()) { m_destImage = m_orgImage; return; } d->div = m_orgImage.sixteenBit() ? 65535.0 : 255.0; d->leadLumaNoise = d->settings.lumaIntensity * (m_orgImage.sixteenBit() ? 256.0 : 1.0); d->leadChromaBlueNoise = d->settings.chromaBlueIntensity * (m_orgImage.sixteenBit() ? 256.0 : 1.0); d->leadChromaRedNoise = d->settings.chromaRedIntensity * (m_orgImage.sixteenBit() ? 256.0 : 1.0); d->generator.seed(1); // noise will always be the same QList<int> vals = multithreadedSteps(m_orgImage.width()); QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { tasks.append(QtConcurrent::run(this, &FilmGrainFilter::filmgrainMultithreaded, vals[j], vals[j+1] )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); }
bool CharcoalFilter::convolveImage(const unsigned int order, const double* kernel) { long kernelWidth = order; if ((kernelWidth % 2) == 0) { qCWarning(DIGIKAM_DIMG_LOG) << "Kernel width must be an odd number!"; return false; } long i; double normalize = 0.0; QScopedArrayPointer<double> normal_kernel(new double[kernelWidth * kernelWidth]); if (!normal_kernel) { qCWarning(DIGIKAM_DIMG_LOG) << "Unable to allocate memory!"; 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]; } // -------------------------------------------------------- QList<int> vals = multithreadedSteps(m_orgImage.height()); QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { tasks.append(QtConcurrent::run(this, &CharcoalFilter::convolveImageMultithreaded, vals[j], vals[j+1], normal_kernel.data(), kernelWidth )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); return true; }
/* Function to apply the circular waves effect backported from ImageProcesqSing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * X, Y => Position of circle center on the image. * Amplitude => Sinoidal maximum height * Frequency => Frequency value. * Phase => Phase value. * WavesType => If true the amplitude is proportional to radius. * Antialias => Smart bluring result. * * Theory => Similar to Waves effect, but here I apply a senoidal function * with the angle point. */ void DistortionFXFilter::circularWaves(DImg* orgImage, DImg* destImage, int X, int Y, double Amplitude, double Frequency, double Phase, bool WavesType, bool AntiAlias) { if (Amplitude < 0.0) { Amplitude = 0.0; } if (Frequency < 0.0) { Frequency = 0.0; } int progress; QList<int> vals = multithreadedSteps(orgImage->width()); QList <QFuture<void> > tasks; Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.Phase = Phase; prm.Frequency = Frequency; prm.Amplitude = Amplitude; prm.WavesType = WavesType; prm.X = X; prm.Y = Y; prm.AntiAlias = AntiAlias; for (int h = 0; runningFlag() && (h < (int)orgImage->height()); ++h) { for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; prm.h = h; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::circularWavesMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); // Update the progress bar in dialog. progress = (int)(((double)h * 100.0) / orgImage->height()); if (progress % 5 == 0) { postProgress(progress); } } }
/* Function to apply the Cilindrical effect backported from ImageProcessing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Coeff => Cilindrical value. * Horizontal => Apply horizontally. * Vertical => Apply vertically. * Antialias => Smart blurring result. * * Theory => This is a great effect, similar to Spherize (Photoshop). * If you understand FishEye, you will understand Cilindrical * FishEye apply a logarithm function using a sphere radius, * Spherize use the same function but in a rectangular * environment. */ void DistortionFXFilter::cilindrical(DImg* orgImage, DImg* destImage, double Coeff, bool Horizontal, bool Vertical, bool AntiAlias) { if ((Coeff == 0.0) || (!(Horizontal || Vertical))) { return; } int progress; // initial copy memcpy(destImage->bits(), orgImage->bits(), orgImage->numBytes()); QList<int> vals = multithreadedSteps(orgImage->width()); QList <QFuture<void> > tasks; Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.Coeff = Coeff; prm.Horizontal = Horizontal; prm.Vertical = Vertical; prm.AntiAlias = AntiAlias; // main loop for (int h = 0; runningFlag() && (h < (int)orgImage->height()); ++h) { for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; prm.h = h; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::cilindricalMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); // Update the progress bar in dialog. progress = (int)(((double)h * 100.0) / orgImage->height()); if (progress % 5 == 0) { postProgress(progress); } } }
/* Function to apply the block waves effect * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Amplitude => Sinoidal maximum height * Frequency => Frequency value * Mode => The mode to be applied. * * Theory => This is an amazing effect, very funny when amplitude and * frequency are small values. */ void DistortionFXFilter::blockWaves(DImg* orgImage, DImg* destImage, int Amplitude, int Frequency, bool Mode) { if (Amplitude < 0) { Amplitude = 0; } if (Frequency < 0) { Frequency = 0; } int progress; QList<int> vals = multithreadedSteps(orgImage->height()); QList <QFuture<void> > tasks; Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.Mode = Mode; prm.Frequency = Frequency; prm.Amplitude = Amplitude; for (int w = 0; runningFlag() && (w < (int)orgImage->width()); ++w) { for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; prm.w = w; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::blockWavesMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); // Update the progress bar in dialog. progress = (int)(((double)w * 100.0) / orgImage->width()); if (progress % 5 == 0) { postProgress(progress); } } }
/* Function to apply the twirl effect backported from ImageProcesqSing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * dist => Distance value. * Antialias => Smart blurring result. * * Theory => Take spiral studies, you will understand better, I'm studying * hard on this effect, because it is not too fast. */ void DistortionFXFilter::twirl(DImg* orgImage, DImg* destImage, int dist, bool AntiAlias) { // if dist value is zero, we do nothing if (dist == 0) { return; } int progress; QList<int> vals = multithreadedSteps(orgImage->width()); QList <QFuture<void> > tasks; Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.dist = dist; prm.AntiAlias = AntiAlias; // main loop for (int h = 0; runningFlag() && (h < (int)orgImage->height()); ++h) { for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; prm.h = h; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::twirlMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); // Update the progress bar in dialog. progress = (int)(((double)h * 100.0) / orgImage->height()); if (progress % 5 == 0) { postProgress(progress); } } }
/* Function to apply the tile effect * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * WSize => Tile Width * HSize => Tile Height * Random => Maximum random value * * Theory => Similar to Tile effect from Photoshop and very easy to * understand. We get a rectangular area uqSing WSize and HSize and * replace in a position with a random distance from the original * position. */ void DistortionFXFilter::tile(DImg* orgImage, DImg* destImage, int WSize, int HSize, int Random) { if (WSize < 1) { WSize = 1; } if (HSize < 1) { HSize = 1; } if (Random < 1) { Random = 1; } Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.WSize = WSize; prm.HSize = HSize; prm.Random = Random; d->generator.seed(d->randomSeed); QList<int> vals = multithreadedSteps(orgImage->height()); QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::tileMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); }
bool SharpenFilter::convolveImage(const unsigned int order, const double* const kernel) { uint y; int progress; long i; double normalize = 0.0; Args prm; prm.kernelWidth = order; prm.halfKernelWidth = prm.kernelWidth / 2;; if ((prm.kernelWidth % 2) == 0) { qCWarning(DIGIKAM_DIMG_LOG) << "Kernel width must be an odd number!"; return false; } QScopedArrayPointer<double> normal_kernel(new double[prm.kernelWidth * prm.kernelWidth]); if (normal_kernel.isNull()) { qCWarning(DIGIKAM_DIMG_LOG) << "Unable to allocate memory!"; return false; } for (i = 0 ; i < (prm.kernelWidth * prm.kernelWidth) ; ++i) { normalize += kernel[i]; } if (fabs(normalize) <= Epsilon) { normalize = 1.0; } normalize = 1.0 / normalize; for (i = 0 ; i < (prm.kernelWidth * prm.kernelWidth) ; ++i) { normal_kernel[i] = normalize * kernel[i]; } prm.normal_kernel = normal_kernel.data(); QList<int> vals = multithreadedSteps(m_destImage.width()); for (y = 0 ; runningFlag() && (y < m_destImage.height()) ; ++y) { QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; prm.y = y; tasks.append(QtConcurrent::run(this, &SharpenFilter::convolveImageMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); progress = (int)(((double)y * 100.0) / m_destImage.height()); if (progress % 5 == 0) { postProgress(progress); } } return true; }
void NRFilter::waveletDenoise(float* fimg[3], unsigned int width, unsigned int height, float threshold, double softness) { float thold; uint lpass = 0, hpass = 0; double stdev[5]; uint samples[5]; uint size = width * height; QScopedArrayPointer<float> temp(new float[qMax(width, height)]); QList<int> vals = multithreadedSteps(size); QList <QFuture<void> > tasks; Args prm; prm.thold = &thold; prm.lpass = &lpass; prm.hpass = &hpass; prm.threshold = threshold; prm.softness = softness; prm.stdev = &stdev[0]; prm.samples = &samples[0]; prm.fimg = fimg; for (uint lev = 0; runningFlag() && (lev < 5); ++lev) { lpass = ((lev & 1) + 1); for (uint row = 0; runningFlag() && (row < height); ++row) { hatTransform(temp.data(), fimg[hpass] + row * width, 1, width, 1 << lev); for (uint col = 0; col < width; ++col) { fimg[lpass][row * width + col] = temp[col] * 0.25; } } for (uint col = 0; runningFlag() && (col < width); ++col) { hatTransform(temp.data(), fimg[lpass] + col, width, height, 1 << lev); for (uint row = 0; row < height; ++row) { fimg[lpass][row * width + col] = temp[row] * 0.25; } } thold = 5.0 / (1 << 6) * exp(-2.6 * sqrt(lev + 1.0)) * 0.8002 / exp(-2.6); // initialize stdev values for all intensities stdev[0] = stdev[1] = stdev[2] = stdev[3] = stdev[4] = 0.0; samples[0] = samples[1] = samples[2] = samples[3] = samples[4] = 0; // calculate stdevs for all intensities for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; tasks.append(QtConcurrent::run(this, &NRFilter::calculteStdevMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); stdev[0] = sqrt(stdev[0] / (samples[0] + 1)); stdev[1] = sqrt(stdev[1] / (samples[1] + 1)); stdev[2] = sqrt(stdev[2] / (samples[2] + 1)); stdev[3] = sqrt(stdev[3] / (samples[3] + 1)); stdev[4] = sqrt(stdev[4] / (samples[4] + 1)); // do thresholding tasks.clear(); for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; tasks.append(QtConcurrent::run(this, &NRFilter::thresholdingMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); hpass = lpass; } for (uint i = 0; runningFlag() && (i < size); ++i) { fimg[0][i] = fimg[0][i] + fimg[lpass][i]; } }