diff --git a/libs/image/kis_edge_detection_kernel.cpp b/libs/image/kis_edge_detection_kernel.cpp index 3cb998e010..a15a6b9fb8 100644 --- a/libs/image/kis_edge_detection_kernel.cpp +++ b/libs/image/kis_edge_detection_kernel.cpp @@ -1,652 +1,659 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * 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. */ #include "kis_edge_detection_kernel.h" -#include "kis_global.h" #include "kis_convolution_kernel.h" -#include +#include "kis_global.h" +#include #include #include -#include -#include #include +#include +#include #include -KisEdgeDetectionKernel::KisEdgeDetectionKernel() -{ - -} +KisEdgeDetectionKernel::KisEdgeDetectionKernel() {} /* * This code is very similar to the gaussian kernel code, except unlike the gaussian code, * edge-detection kernels DO use the diagonals. * Except for the simple mode. We implement the simple mode because it is an analog to * the old sobel filter. */ Eigen::Matrix KisEdgeDetectionKernel::createHorizontalMatrix(qreal radius, FilterType type, bool reverse) { int kernelSize = kernelSizeFromRadius(radius); Eigen::Matrix matrix(kernelSize, kernelSize); KIS_ASSERT_RECOVER_NOOP(kernelSize & 0x1); const int center = kernelSize / 2; - if (type==Prewit) { + if (type == Prewit) { for (int x = 0; x < kernelSize; x++) { - for (int y=0; y KisEdgeDetectionKernel::createVerticalMatrix(qreal radius, FilterType type, bool reverse) { int kernelSize = kernelSizeFromRadius(radius); Eigen::Matrix matrix(kernelSize, kernelSize); KIS_ASSERT_RECOVER_NOOP(kernelSize & 0x1); const int center = kernelSize / 2; - if (type==Prewit) { + if (type == Prewit) { for (int y = 0; y < kernelSize; y++) { - for (int x=0; x matrix = createHorizontalMatrix(radius, type, reverse); if (denormalize) { return KisConvolutionKernel::fromMatrix(matrix, 0.5, 1); } else { return KisConvolutionKernel::fromMatrix(matrix, 0, matrix.sum()); } } KisConvolutionKernelSP KisEdgeDetectionKernel::createVerticalKernel(qreal radius, KisEdgeDetectionKernel::FilterType type, bool denormalize, bool reverse) { Eigen::Matrix matrix = createVerticalMatrix(radius, type, reverse); if (denormalize) { return KisConvolutionKernel::fromMatrix(matrix, 0.5, 1); } else { return KisConvolutionKernel::fromMatrix(matrix, 0, matrix.sum()); } } int KisEdgeDetectionKernel::kernelSizeFromRadius(qreal radius) { return qMax((int)(2 * ceil(sigmaFromRadius(radius)) + 1), 3); } qreal KisEdgeDetectionKernel::sigmaFromRadius(qreal radius) { return 0.3 * radius + 0.3; } void KisEdgeDetectionKernel::applyEdgeDetection(KisPaintDeviceSP device, - const QRect &rect, + const QRect& rect, qreal xRadius, qreal yRadius, KisEdgeDetectionKernel::FilterType type, - const QBitArray &channelFlags, - KoUpdater *progressUpdater, + const QBitArray& channelFlags, + KoUpdater* progressUpdater, FilterOutput output, bool writeToAlpha) { QPoint srcTopLeft = rect.topLeft(); KisPainter finalPainter(device); finalPainter.setChannelFlags(channelFlags); finalPainter.setProgress(progressUpdater); if (output == pythagorean || output == radian) { KisPaintDeviceSP x_denormalised = new KisPaintDevice(device->colorSpace()); KisPaintDeviceSP y_denormalised = new KisPaintDevice(device->colorSpace()); x_denormalised->prepareClone(device); y_denormalised->prepareClone(device); KisConvolutionKernelSP kernelHorizLeftRight = KisEdgeDetectionKernel::createHorizontalKernel(xRadius, type); KisConvolutionKernelSP kernelVerticalTopBottom = KisEdgeDetectionKernel::createVerticalKernel(yRadius, type); qreal horizontalCenter = qreal(kernelHorizLeftRight->width()) / 2.0; qreal verticalCenter = qreal(kernelVerticalTopBottom->height()) / 2.0; KisConvolutionPainter horizPainterLR(x_denormalised); horizPainterLR.setChannelFlags(channelFlags); horizPainterLR.setProgress(progressUpdater); - horizPainterLR.applyMatrix(kernelHorizLeftRight, device, + horizPainterLR.applyMatrix(kernelHorizLeftRight, + device, srcTopLeft - QPoint(0, ceil(horizontalCenter)), srcTopLeft - QPoint(0, ceil(horizontalCenter)), - rect.size() + QSize(0, 2 * ceil(horizontalCenter)), BORDER_REPEAT); - + rect.size() + QSize(0, 2 * ceil(horizontalCenter)), + BORDER_REPEAT); KisConvolutionPainter verticalPainterTB(y_denormalised); verticalPainterTB.setChannelFlags(channelFlags); verticalPainterTB.setProgress(progressUpdater); - verticalPainterTB.applyMatrix(kernelVerticalTopBottom, device, + verticalPainterTB.applyMatrix(kernelVerticalTopBottom, + device, srcTopLeft - QPoint(0, ceil(verticalCenter)), srcTopLeft - QPoint(0, ceil(verticalCenter)), - rect.size() + QSize(0, 2 * ceil(verticalCenter)), BORDER_REPEAT); + rect.size() + QSize(0, 2 * ceil(verticalCenter)), + BORDER_REPEAT); KisSequentialIterator yItterator(y_denormalised, rect); KisSequentialIterator xItterator(x_denormalised, rect); KisSequentialIterator finalIt(device, rect); const int pixelSize = device->colorSpace()->pixelSize(); const int channels = device->colorSpace()->channelCount(); const int alphaPos = device->colorSpace()->alphaPos(); KIS_SAFE_ASSERT_RECOVER_RETURN(alphaPos >= 0); QVector yNormalised(channels); QVector xNormalised(channels); QVector finalNorm(channels); - while(yItterator.nextPixel() && xItterator.nextPixel() && finalIt.nextPixel()) { + while (yItterator.nextPixel() && xItterator.nextPixel() && finalIt.nextPixel()) { device->colorSpace()->normalisedChannelsValue(yItterator.rawData(), yNormalised); device->colorSpace()->normalisedChannelsValue(xItterator.rawData(), xNormalised); device->colorSpace()->normalisedChannelsValue(finalIt.rawData(), finalNorm); if (output == pythagorean) { - for (int c = 0; ccolorSpace()); qreal alpha = 0; - for (int c = 0; c<(channels-1); c++) { - alpha = alpha+finalNorm[c]; + for (int c = 0; c < (channels - 1); c++) { + alpha = alpha + finalNorm[c]; } - alpha = qMin(alpha/(channels-1), col.opacityF()); + alpha = qMin(alpha / (channels - 1), col.opacityF()); col.setOpacity(alpha); memcpy(finalIt.rawData(), col.data(), pixelSize); } else { quint8* f = finalIt.rawData(); finalNorm[alphaPos] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(f, finalNorm); memcpy(finalIt.rawData(), f, pixelSize); } - } } else { KisConvolutionKernelSP kernel; qreal center = 0; bool denormalize = !writeToAlpha; if (output == xGrowth) { kernel = KisEdgeDetectionKernel::createHorizontalKernel(xRadius, type, denormalize); center = qreal(kernel->width()) / 2.0; } else if (output == xFall) { kernel = KisEdgeDetectionKernel::createHorizontalKernel(xRadius, type, denormalize, true); center = qreal(kernel->width()) / 2.0; } else if (output == yGrowth) { kernel = KisEdgeDetectionKernel::createVerticalKernel(yRadius, type, denormalize); center = qreal(kernel->height()) / 2.0; - } else { //yFall + } else { // yFall kernel = KisEdgeDetectionKernel::createVerticalKernel(yRadius, type, denormalize, true); center = qreal(kernel->height()) / 2.0; } if (writeToAlpha) { KisPaintDeviceSP denormalised = new KisPaintDevice(device->colorSpace()); denormalised->prepareClone(device); KisConvolutionPainter kernelP(denormalised); kernelP.setChannelFlags(channelFlags); kernelP.setProgress(progressUpdater); - kernelP.applyMatrix(kernel, device, + kernelP.applyMatrix(kernel, + device, srcTopLeft - QPoint(0, ceil(center)), srcTopLeft - QPoint(0, ceil(center)), - rect.size() + QSize(0, 2 * ceil(center)), BORDER_REPEAT); + rect.size() + QSize(0, 2 * ceil(center)), + BORDER_REPEAT); KisSequentialIterator iterator(denormalised, rect); KisSequentialIterator finalIt(device, rect); const int pixelSize = device->colorSpace()->pixelSize(); const int channels = device->colorSpace()->colorChannelCount(); QVector normalised(channels); while (iterator.nextPixel() && finalIt.nextPixel()) { device->colorSpace()->normalisedChannelsValue(iterator.rawData(), normalised); KoColor col(finalIt.rawData(), device->colorSpace()); qreal alpha = 0; - for (int c = 0; ccolorSpace()->setOpacity(finalIt.rawData(), 1.0, numConseqPixels); } } } } void KisEdgeDetectionKernel::applyCannyEdgeDetection(KisPaintDeviceSP _device, const QRect& rect, qreal xRadius, qreal yRadius, const QBitArray& channelFlags, KoUpdater* progressUpdater, - bool) + int thresholdMax, + int thresholdMin) { QPoint srcTopLeft = rect.topLeft(); auto bounds = _device->defaultBounds(); auto device = KisPainter::convertToAlphaAsGray(_device); device->setDefaultBounds(bounds); KisPaintDeviceSP x_denormalised = new KisPaintDevice(device->colorSpace()); KisPaintDeviceSP y_denormalised = new KisPaintDevice(device->colorSpace()); x_denormalised->prepareClone(device); y_denormalised->prepareClone(device); KisConvolutionKernelSP kernelHorizLeftRight = KisEdgeDetectionKernel::createHorizontalKernel(xRadius, KisEdgeDetectionKernel::SobelVector); KisConvolutionKernelSP kernelVerticalTopBottom = KisEdgeDetectionKernel::createVerticalKernel(yRadius, KisEdgeDetectionKernel::SobelVector); qreal horizontalCenter = qreal(kernelHorizLeftRight->width()) / 2.0; qreal verticalCenter = qreal(kernelVerticalTopBottom->height()) / 2.0; KisConvolutionPainter horizPainterLR(x_denormalised); horizPainterLR.setChannelFlags(channelFlags); horizPainterLR.setProgress(progressUpdater); horizPainterLR.applyMatrix(kernelHorizLeftRight, device, srcTopLeft - QPoint(0, ceil(horizontalCenter)), srcTopLeft - QPoint(0, ceil(horizontalCenter)), rect.size() + QSize(0, 2 * ceil(horizontalCenter)), BORDER_REPEAT); KisConvolutionPainter verticalPainterTB(y_denormalised); verticalPainterTB.setChannelFlags(channelFlags); verticalPainterTB.setProgress(progressUpdater); verticalPainterTB.applyMatrix(kernelVerticalTopBottom, device, srcTopLeft - QPoint(0, ceil(verticalCenter)), srcTopLeft - QPoint(0, ceil(verticalCenter)), rect.size() + QSize(0, 2 * ceil(verticalCenter)), BORDER_REPEAT); KisSequentialIterator yIterator(y_denormalised, rect); KisSequentialIterator xIterator(x_denormalised, rect); KisSequentialIterator sobelIterator(device, rect); QVector yNormalised(1); QVector xNormalised(1); QVector finalNorm(1); while (yIterator.nextPixel() && xIterator.nextPixel() && sobelIterator.nextPixel()) { device->colorSpace()->normalisedChannelsValue(yIterator.rawData(), yNormalised); device->colorSpace()->normalisedChannelsValue(xIterator.rawData(), xNormalised); device->colorSpace()->normalisedChannelsValue(sobelIterator.rawData(), finalNorm); finalNorm[0] = 2 * sqrt(((xNormalised[0] - 0.5) * (xNormalised[0] - 0.5)) + ((yNormalised[0] - 0.5) * (yNormalised[0] - 0.5))); quint8* f = sobelIterator.rawData(); device->colorSpace()->fromNormalisedChannelsValue(f, finalNorm); memcpy(sobelIterator.rawData(), f, device->colorSpace()->pixelSize()); } KisRandomAccessorSP randomSobel = device->createRandomAccessorNG(rect.x(), rect.y()); KisRandomConstAccessorSP gx_iter = x_denormalised->createRandomConstAccessorNG(rect.x(), rect.y()); KisRandomConstAccessorSP gy_iter = x_denormalised->createRandomConstAccessorNG(rect.x(), rect.y()); for (int x = rect.x() + 1; x < rect.x() + rect.width() - 1; x++) { for (int y = rect.y() + 1; y < rect.y() + rect.height() - 1; y++) { randomSobel->moveTo(x, y); const float current = *randomSobel->rawData(); float firstIntensity, secondIntensity; gx_iter->moveTo(x, y); gy_iter->moveTo(x, y); const float direction = atan2(*gx_iter->rawDataConst(), *gy_iter->rawDataConst()); if (direction <= 1 || direction > 7) { randomSobel->moveTo(x + 1, y); firstIntensity = *randomSobel->rawData(); randomSobel->moveTo(x - 1, y); secondIntensity = *randomSobel->rawData(); } if (direction > 1 || direction <= 3) { randomSobel->moveTo(x + 1, y + 1); firstIntensity = *randomSobel->rawData(); randomSobel->moveTo(x - 1, y - 1); secondIntensity = *randomSobel->rawData(); } if (direction > 3 || direction <= 5) { randomSobel->moveTo(x, y + 1); firstIntensity = *randomSobel->rawData(); randomSobel->moveTo(x, y - 1); secondIntensity = *randomSobel->rawData(); } if (direction > 5 || direction <= 7) { randomSobel->moveTo(x + 1, y - 1); firstIntensity = *randomSobel->rawData(); randomSobel->moveTo(x - 1, y + 1); secondIntensity = *randomSobel->rawData(); } randomSobel->moveTo(x, y); if (2 * current <= (firstIntensity + secondIntensity)) { quint8* temp = new quint8(); finalNorm[0] = 0.0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); } } } - quint8 thresholdMax = 200; - quint8 thresholdMin = 100; - for (int x = rect.x() + 1; x < rect.x() + rect.width() - 1; x++) { for (int y = rect.y() + 1; y < rect.y() + rect.height() - 1; y++) { randomSobel->moveTo(x, y); quint8 intensity = *randomSobel->rawData(); if (intensity < thresholdMin) { quint8* temp = new quint8(); finalNorm[0] = 0.0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); } else if (intensity < thresholdMax) { randomSobel->moveTo(x + 1, y); quint8 intensity = *randomSobel->rawData(); if (intensity > thresholdMax) { quint8* temp = new quint8(); finalNorm[0] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); continue; } randomSobel->moveTo(x - 1, y); intensity = *randomSobel->rawData(); if (intensity > thresholdMax) { quint8* temp = new quint8(); finalNorm[0] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); continue; } randomSobel->moveTo(x, y + 1); intensity = *randomSobel->rawData(); if (intensity > thresholdMax) { quint8* temp = new quint8(); finalNorm[0] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); continue; } randomSobel->moveTo(x, y - 1); intensity = *randomSobel->rawData(); if (intensity > thresholdMax) { quint8* temp = new quint8(); finalNorm[0] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); continue; } randomSobel->moveTo(x + 1, y + 1); intensity = *randomSobel->rawData(); if (intensity > thresholdMax) { quint8* temp = new quint8(); finalNorm[0] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); continue; } randomSobel->moveTo(x - 1, y - 1); intensity = *randomSobel->rawData(); if (intensity > thresholdMax) { quint8* temp = new quint8(); finalNorm[0] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); continue; } randomSobel->moveTo(x + 1, y - 1); intensity = *randomSobel->rawData(); if (intensity > thresholdMax) { quint8* temp = new quint8(); finalNorm[0] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); continue; } randomSobel->moveTo(x - 1, y + 1); intensity = *randomSobel->rawData(); if (intensity > thresholdMax) { quint8* temp = new quint8(); finalNorm[0] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); continue; } } else { quint8* temp = new quint8(); finalNorm[0] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); } } } KisPainter::copyAreaOptimized(rect.topLeft(), device, _device, rect); } void KisEdgeDetectionKernel::convertToNormalMap(KisPaintDeviceSP device, - const QRect &rect, + const QRect& rect, qreal xRadius, qreal yRadius, KisEdgeDetectionKernel::FilterType type, int channelToConvert, QVector channelOrder, QVector channelFlip, - const QBitArray &channelFlags, - KoUpdater *progressUpdater) + const QBitArray& channelFlags, + KoUpdater* progressUpdater) { QPoint srcTopLeft = rect.topLeft(); KisPainter finalPainter(device); finalPainter.setChannelFlags(channelFlags); finalPainter.setProgress(progressUpdater); KisPaintDeviceSP x_denormalised = new KisPaintDevice(device->colorSpace()); KisPaintDeviceSP y_denormalised = new KisPaintDevice(device->colorSpace()); x_denormalised->prepareClone(device); y_denormalised->prepareClone(device); - KisConvolutionKernelSP kernelHorizLeftRight = KisEdgeDetectionKernel::createHorizontalKernel(yRadius, type, true, !channelFlip[1]); - KisConvolutionKernelSP kernelVerticalTopBottom = KisEdgeDetectionKernel::createVerticalKernel(xRadius, type, true, !channelFlip[0]); + KisConvolutionKernelSP kernelHorizLeftRight = + KisEdgeDetectionKernel::createHorizontalKernel(yRadius, type, true, !channelFlip[1]); + KisConvolutionKernelSP kernelVerticalTopBottom = + KisEdgeDetectionKernel::createVerticalKernel(xRadius, type, true, !channelFlip[0]); qreal horizontalCenter = qreal(kernelHorizLeftRight->width()) / 2.0; qreal verticalCenter = qreal(kernelVerticalTopBottom->height()) / 2.0; KisConvolutionPainter horizPainterLR(y_denormalised); horizPainterLR.setChannelFlags(channelFlags); horizPainterLR.setProgress(progressUpdater); - horizPainterLR.applyMatrix(kernelHorizLeftRight, device, + horizPainterLR.applyMatrix(kernelHorizLeftRight, + device, srcTopLeft - QPoint(ceil(horizontalCenter), 0), srcTopLeft - QPoint(ceil(horizontalCenter), 0), - rect.size() + QSize(2 * ceil(horizontalCenter), 0), BORDER_REPEAT); - + rect.size() + QSize(2 * ceil(horizontalCenter), 0), + BORDER_REPEAT); KisConvolutionPainter verticalPainterTB(x_denormalised); verticalPainterTB.setChannelFlags(channelFlags); verticalPainterTB.setProgress(progressUpdater); - verticalPainterTB.applyMatrix(kernelVerticalTopBottom, device, + verticalPainterTB.applyMatrix(kernelVerticalTopBottom, + device, srcTopLeft - QPoint(0, ceil(verticalCenter)), srcTopLeft - QPoint(0, ceil(verticalCenter)), - rect.size() + QSize(0, 2 * ceil(verticalCenter)), BORDER_REPEAT); + rect.size() + QSize(0, 2 * ceil(verticalCenter)), + BORDER_REPEAT); KisSequentialIterator yItterator(y_denormalised, rect); KisSequentialIterator xItterator(x_denormalised, rect); KisSequentialIterator finalIt(device, rect); const int pixelSize = device->colorSpace()->pixelSize(); const int channels = device->colorSpace()->channelCount(); const int alphaPos = device->colorSpace()->alphaPos(); KIS_SAFE_ASSERT_RECOVER_RETURN(alphaPos >= 0); QVector yNormalised(channels); QVector xNormalised(channels); QVector finalNorm(channels); - while(yItterator.nextPixel() && xItterator.nextPixel() && finalIt.nextPixel()) { + while (yItterator.nextPixel() && xItterator.nextPixel() && finalIt.nextPixel()) { device->colorSpace()->normalisedChannelsValue(yItterator.rawData(), yNormalised); device->colorSpace()->normalisedChannelsValue(xItterator.rawData(), xNormalised); qreal z = 1.0; - if (channelFlip[2]==true){ - z=-1.0; + if (channelFlip[2] == true) { + z = -1.0; } - QVector3D normal = QVector3D((xNormalised[channelToConvert]-0.5)*2, (yNormalised[channelToConvert]-0.5)*2, z); + QVector3D normal = + QVector3D((xNormalised[channelToConvert] - 0.5) * 2, (yNormalised[channelToConvert] - 0.5) * 2, z); normal.normalize(); finalNorm.fill(1.0); - for (int c = 0; c<3; c++) { - finalNorm[device->colorSpace()->channels().at(channelOrder[c])->displayPosition()] = (normal[channelOrder[c]]/2)+0.5; + for (int c = 0; c < 3; c++) { + finalNorm[device->colorSpace()->channels().at(channelOrder[c])->displayPosition()] = + (normal[channelOrder[c]] / 2) + 0.5; } - finalNorm[alphaPos]= 1.0; + finalNorm[alphaPos] = 1.0; quint8* pixel = finalIt.rawData(); device->colorSpace()->fromNormalisedChannelsValue(pixel, finalNorm); memcpy(finalIt.rawData(), pixel, pixelSize); - } } diff --git a/libs/image/kis_edge_detection_kernel.h b/libs/image/kis_edge_detection_kernel.h index b412e5ce0a..ce1f6e7092 100644 --- a/libs/image/kis_edge_detection_kernel.h +++ b/libs/image/kis_edge_detection_kernel.h @@ -1,151 +1,162 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * 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. */ #ifndef KIS_EDGE_DETECTION_KERNEL_H #define KIS_EDGE_DETECTION_KERNEL_H -#include "kritaimage_export.h" #include "kis_types.h" +#include "kritaimage_export.h" #include class QRect; class KRITAIMAGE_EXPORT KisEdgeDetectionKernel { -public: + public: KisEdgeDetectionKernel(); - enum FilterType { - Simple, //A weird simple method used in our old sobel filter - Prewit, //The simpler prewitt detection, which doesn't smooth. - SobelVector //Sobel does smooth. The creation of bigger kernels is based on an approach regarding vectors. + enum FilterType + { + Simple, // A weird simple method used in our old sobel filter + Prewit, // The simpler prewitt detection, which doesn't smooth. + SobelVector // Sobel does smooth. The creation of bigger kernels is based on an approach regarding vectors. }; - enum FilterOutput { + enum FilterOutput + { pythagorean, xGrowth, xFall, yGrowth, yFall, radian }; /** * @brief createHorizontalMatrix * @param radius the radius. 1 makes a 3x3 kernel. * @param type One of the entries in the enum Filtertype * @param reverse which direction the gradient goes. * The horizontal gradient by default detects the rightmost edges. * Reversed it selects the leftmost edges. * @return */ - static Eigen::Matrix - createHorizontalMatrix(qreal radius, FilterType type, bool reverse = false); + static Eigen::Matrix createHorizontalMatrix(qreal radius, + FilterType type, + bool reverse = false); /** * @brief createVerticalMatrix * @param radius the radius. 1 makes a 3x3 kernel. * @param type One of the entries in the enum Filtertype * @param reverse which direction the gradient goes. * The vertical gradient by default detects the topmost edges. * Reversed it selects the bottommost edges. * @return */ - static Eigen::Matrix - createVerticalMatrix(qreal radius, FilterType type, bool reverse = false); + static Eigen::Matrix createVerticalMatrix(qreal radius, + FilterType type, + bool reverse = false); - static KisConvolutionKernelSP - createHorizontalKernel(qreal radius, FilterType type, bool denormalize = true, bool reverse = false); + static KisConvolutionKernelSP createHorizontalKernel(qreal radius, + FilterType type, + bool denormalize = true, + bool reverse = false); - static KisConvolutionKernelSP - createVerticalKernel(qreal radius, FilterType type, bool denormalize = true, bool reverse = false); + static KisConvolutionKernelSP createVerticalKernel(qreal radius, + FilterType type, + bool denormalize = true, + bool reverse = false); static int kernelSizeFromRadius(qreal radius); static qreal sigmaFromRadius(qreal radius); /** * @brief applyEdgeDetection * This applies the edge detection filter to the device. * @param device the device to apply to. * @param rect the affected rect. * @param xRadius the radius of the horizontal sampling, radius of 0 is effectively disabling it. * @param yRadius the radius of the vertical sampling, refius of 0 is effectively disabling it. * @param type the type can be prewitt, sobel or simple, each of which * have a different sampling for the eventual edge detection. * @param channelFlags the affected channels. * @param progressUpdater the progress updater if it exists. * @param output the output mode. * @param writeToAlpha whether or not to have the result applied to the transparency than the color channels, * this is useful for fringe effects. */ static void applyEdgeDetection(KisPaintDeviceSP device, - const QRect& rect, - qreal xRadius, qreal yRadius, - FilterType type, - const QBitArray &channelFlags, - KoUpdater *progressUpdater, - FilterOutput output = pythagorean, - bool writeToAlpha = false); + const QRect& rect, + qreal xRadius, + qreal yRadius, + FilterType type, + const QBitArray& channelFlags, + KoUpdater* progressUpdater, + FilterOutput output = pythagorean, + bool writeToAlpha = false); /** * @brief applyCannyEdgeDetection * This applies the canny edge detection filter to the device. * @param device the device to apply to. * @param rect the affected rect. * @param xRadius the radius of the horizontal sampling, radius of 0 is effectively disabling it. * @param yRadius the radius of the vertical sampling, refius of 0 is effectively disabling it. * @param channelFlags the affected channels. * @param progressUpdater the progress updater if it exists. * @param writeToAlpha whether or not to have the result applied to the transparency than the color channels, * this is useful for fringe effects. */ static void applyCannyEdgeDetection(KisPaintDeviceSP device, - const QRect& rect, - qreal xRadius, qreal yRadius, - const QBitArray &channelFlags, - KoUpdater *progressUpdater, - bool writeToAlpha = false); + const QRect& rect, + qreal xRadius, + qreal yRadius, + const QBitArray& channelFlags, + KoUpdater* progressUpdater, + int thresholdMax, + int thresholdMin); /** * @brief converToNormalMap * Convert a channel of the device to a normal map. The channel will be interpreted as a heightmap. * @param device the device * @param rect the rectangle to apply this to. * @param xRadius the xradius * @param yRadius the yradius * @param type the edge detection filter. * @param channelToConvert the channel to use as a grayscale. * @param channelOrder the order in which the xyz coordinates ought to be written to the pixels. * @param channelFlip whether to flip the channels * @param channelFlags the channel flags * @param progressUpdater */ static void convertToNormalMap(KisPaintDeviceSP device, - const QRect & rect, - qreal xRadius, - qreal yRadius, - FilterType type, - int channelToConvert, - QVector channelOrder, - QVector channelFlip, - const QBitArray &channelFlags, - KoUpdater *progressUpdater); + const QRect& rect, + qreal xRadius, + qreal yRadius, + FilterType type, + int channelToConvert, + QVector channelOrder, + QVector channelFlip, + const QBitArray& channelFlags, + KoUpdater* progressUpdater); }; #endif // KIS_EDGE_DETECTION_KERNEL_H diff --git a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp index b6403f4ca3..94541cab63 100644 --- a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp +++ b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp @@ -1,164 +1,185 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * 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. */ #include "kis_edge_detection_filter.h" #include "kis_wdg_edge_detection.h" -#include #include #include +#include -#include #include +#include +#include "kis_lod_transform.h" #include #include -#include #include #include -#include "kis_lod_transform.h" +#include #include -#include #include +#include -K_PLUGIN_FACTORY_WITH_JSON(KritaEdgeDetectionFilterFactory, "kritaedgedetection.json", registerPlugin();) +K_PLUGIN_FACTORY_WITH_JSON(KritaEdgeDetectionFilterFactory, + "kritaedgedetection.json", + registerPlugin();) -KritaEdgeDetectionFilter::KritaEdgeDetectionFilter(QObject *parent, const QVariantList &) +KritaEdgeDetectionFilter::KritaEdgeDetectionFilter(QObject* parent, const QVariantList&) : QObject(parent) { KisFilterRegistry::instance()->add(KisFilterSP(new KisEdgeDetectionFilter())); } -KritaEdgeDetectionFilter::~KritaEdgeDetectionFilter() -{ -} +KritaEdgeDetectionFilter::~KritaEdgeDetectionFilter() {} -KisEdgeDetectionFilter::KisEdgeDetectionFilter(): KisFilter(id(), FiltersCategoryEdgeDetectionId, i18n("&Edge Detection...")) +KisEdgeDetectionFilter::KisEdgeDetectionFilter() + : KisFilter(id(), FiltersCategoryEdgeDetectionId, i18n("&Edge Detection...")) { setSupportsPainting(true); setSupportsAdjustmentLayers(true); setSupportsLevelOfDetail(true); setColorSpaceIndependence(FULLY_INDEPENDENT); setShowConfigurationWidget(true); } -void KisEdgeDetectionFilter::processImpl(KisPaintDeviceSP device, const QRect &rect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const +void KisEdgeDetectionFilter::processImpl(KisPaintDeviceSP device, + const QRect& rect, + const KisFilterConfigurationSP config, + KoUpdater* progressUpdater) const { Q_ASSERT(device != 0); KisFilterConfigurationSP configuration = config ? config : new KisFilterConfiguration(id().id(), 1); KisLodTransformScalar t(device); QVariant value; configuration->getProperty("horizRadius", value); qreal horizontalRadius = t.scale(value.toFloat()); configuration->getProperty("vertRadius", value); qreal verticalRadius = t.scale(value.toFloat()); QBitArray channelFlags; if (configuration) { channelFlags = configuration->channelFlags(); } - if(config->getString("type") == "canny") { - KisEdgeDetectionKernel::applyCannyEdgeDetection(device, rect, horizontalRadius, verticalRadius, channelFlags, progressUpdater); + if (config->getString("type") == "canny") { + KisFilterSP gaussian = KisFilterRegistry::instance()->get("gaussian blur"); + KisFilterConfigurationSP gaussianConf = gaussian->defaultConfiguration(); + gaussian->process(device, rect, gaussianConf, progressUpdater); + + int max = configuration->getInt("max_threshold", 200); + int min = configuration->getInt("min_threshold", 100); + KisEdgeDetectionKernel::applyCannyEdgeDetection( + device, rect, horizontalRadius, verticalRadius, channelFlags, progressUpdater, max, min); return; } KisEdgeDetectionKernel::FilterType type = KisEdgeDetectionKernel::SobelVector; if (config->getString("type") == "prewitt") { type = KisEdgeDetectionKernel::Prewit; } if (config->getString("type") == "simple") { type = KisEdgeDetectionKernel::Simple; } KisEdgeDetectionKernel::FilterOutput output = KisEdgeDetectionKernel::pythagorean; if (config->getString("output") == "xGrowth") { output = KisEdgeDetectionKernel::xGrowth; } else if (config->getString("output") == "xFall") { output = KisEdgeDetectionKernel::xFall; } else if (config->getString("output") == "yGrowth") { output = KisEdgeDetectionKernel::yGrowth; } else if (config->getString("output") == "yFall") { output = KisEdgeDetectionKernel::yFall; } else if (config->getString("output") == "radian") { output = KisEdgeDetectionKernel::radian; } KisEdgeDetectionKernel::applyEdgeDetection(device, rect, horizontalRadius, verticalRadius, type, channelFlags, progressUpdater, output, config->getBool("transparency", false)); } KisFilterConfigurationSP KisEdgeDetectionFilter::defaultConfiguration() const { KisFilterConfigurationSP config = factoryConfiguration(); config->setProperty("horizRadius", 1); config->setProperty("vertRadius", 1); config->setProperty("type", "prewitt"); config->setProperty("output", "pythagorean"); config->setProperty("lockAspect", true); config->setProperty("transparency", false); return config; } -KisConfigWidget *KisEdgeDetectionFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const +KisConfigWidget* KisEdgeDetectionFilter::createConfigurationWidget(QWidget* parent, + const KisPaintDeviceSP dev, + bool) const { Q_UNUSED(dev); return new KisWdgEdgeDetection(parent); } -QRect KisEdgeDetectionFilter::neededRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const +QRect KisEdgeDetectionFilter::neededRect(const QRect& rect, const KisFilterConfigurationSP _config, int lod) const { KisLodTransformScalar t(lod); QVariant value; /** * NOTE: integer division by two is done on purpose, * because the kernel size is always odd */ - const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; - const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; + const int halfWidth = _config->getProperty("horizRadius", value) + ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 + : 5; + const int halfHeight = _config->getProperty("vertRadius", value) + ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 + : 5; return rect.adjusted(-halfWidth * 2, -halfHeight * 2, halfWidth * 2, halfHeight * 2); } -QRect KisEdgeDetectionFilter::changedRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const +QRect KisEdgeDetectionFilter::changedRect(const QRect& rect, const KisFilterConfigurationSP _config, int lod) const { KisLodTransformScalar t(lod); QVariant value; - const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; - const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; + const int halfWidth = _config->getProperty("horizRadius", value) + ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 + : 5; + const int halfHeight = _config->getProperty("vertRadius", value) + ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 + : 5; - return rect.adjusted( -halfWidth, -halfHeight, halfWidth, halfHeight); + return rect.adjusted(-halfWidth, -halfHeight, halfWidth, halfHeight); } #include "kis_edge_detection_filter.moc" diff --git a/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp b/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp index be81fcd094..3a0e14438a 100644 --- a/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp +++ b/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp @@ -1,129 +1,181 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * 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. */ #include "kis_wdg_edge_detection.h" -#include #include +#include #include -KisWdgEdgeDetection::KisWdgEdgeDetection(QWidget *parent) : - KisConfigWidget(parent), - ui(new Ui_WidgetEdgeDetection) +KisWdgEdgeDetection::KisWdgEdgeDetection(QWidget* parent) + : KisConfigWidget(parent) + , ui(new Ui_WidgetEdgeDetection) { ui->setupUi(this); - m_types << "prewitt"<< "sobol"<< "simple" << "canny"; + m_types << "prewitt" + << "sobol" + << "simple" + << "canny"; m_types_translatable << i18n("Prewitt") << i18n("Sobel") << i18n("Simple") << i18n("Canny"); - m_output << "pythagorean" << "xGrowth" << "xFall" << "yGrowth" << "yFall" << "radian"; - m_output_translatable << i18n("All sides") - << i18n("Top Edge") - << i18n("Bottom Edge") - << i18n("Right Edge") - << i18n("Left Edge") - << i18n("Direction in Radians"); + m_output << "pythagorean" + << "xGrowth" + << "xFall" + << "yGrowth" + << "yFall" + << "radian"; + m_output_translatable << i18n("All sides") << i18n("Top Edge") << i18n("Bottom Edge") << i18n("Right Edge") + << i18n("Left Edge") << i18n("Direction in Radians"); ui->cmbType->addItems(m_types_translatable); ui->cmbOutput->addItems(m_output_translatable); ui->btnAspect->setKeepAspectRatio(false); ui->sldHorizontalRadius->setRange(1.0, 100.0, 2); ui->sldHorizontalRadius->setPrefix(i18n("Horizontal Radius:")); connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(horizontalRadiusChanged(qreal))); ui->sldVerticalRadius->setRange(1.0, 100.0, 2); ui->sldVerticalRadius->setPrefix(i18n("Vertical Radius:")); connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(verticalRadiusChanged(qreal))); + if (ui->cmbType->currentIndex() != 4) { + ui->sldMaxThreshold->setEnabled(false); + ui->sldMinThreshold->setEnabled(false); + } + + ui->sldMaxThreshold->setRange(1, 255); + ui->sldMaxThreshold->setPrefix(i18n("Max Threshold")); + connect(ui->sldMaxThreshold, &KisSliderSpinBox::valueChanged, this, &KisWdgEdgeDetection::maxThresholdChanged); + + ui->sldMinThreshold->setRange(1, 255); + ui->sldMinThreshold->setPrefix(i18n("Min Threshold:")); + connect(ui->sldMinThreshold, &KisSliderSpinBox::valueChanged, this, &KisWdgEdgeDetection::minThresholdChanged); + + connect(ui->cmbType, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &KisWdgEdgeDetection::detectionTypeChanged); connect(ui->btnAspect, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(aspectLockChanged(bool))); connect(ui->cmbType, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->cmbOutput, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->chkTransparent, SIGNAL(clicked()), this, SIGNAL(sigConfigurationItemChanged())); + connect(ui->sldMinThreshold, SIGNAL(valueChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); + connect(ui->sldMaxThreshold, SIGNAL(valueChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); } KisWdgEdgeDetection::~KisWdgEdgeDetection() { delete ui; } KisPropertiesConfigurationSP KisWdgEdgeDetection::configuration() const { KisFilterConfigurationSP config = new KisFilterConfiguration("edge detection", 1); config->setProperty("horizRadius", ui->sldHorizontalRadius->value()); config->setProperty("vertRadius", ui->sldVerticalRadius->value()); config->setProperty("type", m_types.at(ui->cmbType->currentIndex())); config->setProperty("output", m_output.at(ui->cmbOutput->currentIndex())); config->setProperty("lockAspect", ui->btnAspect->keepAspectRatio()); config->setProperty("transparency", ui->chkTransparent->isChecked()); + config->setProperty("max_threshold", ui->sldMaxThreshold->value()); + config->setProperty("min_threshold", ui->sldMinThreshold->value()); return config; } void KisWdgEdgeDetection::setConfiguration(const KisPropertiesConfigurationSP config) { ui->sldHorizontalRadius->setValue(config->getFloat("horizRadius", 1.0)); ui->sldVerticalRadius->setValue(config->getFloat("vertRadius", 1.0)); int index = 0; - if (m_types.contains(config->getString("type", "prewitt"))){ + if (m_types.contains(config->getString("type", "prewitt"))) { index = m_types.indexOf(config->getString("type", "prewitt")); } ui->cmbType->setCurrentIndex(index); index = 0; - if (m_output.contains(config->getString("output", "pythagorean"))){ + if (m_output.contains(config->getString("output", "pythagorean"))) { index = m_output.indexOf(config->getString("output", "pythagorean")); } ui->cmbOutput->setCurrentIndex(index); ui->chkTransparent->setChecked(config->getBool("transparency", false)); ui->btnAspect->setKeepAspectRatio(config->getBool("lockAspect", false)); + ui->sldMinThreshold->setValue(config->getInt("min_threshold", 100)); + ui->sldMaxThreshold->setValue(config->getInt("max_threshold", 200)); } void KisWdgEdgeDetection::horizontalRadiusChanged(qreal r) { ui->sldHorizontalRadius->blockSignals(true); ui->sldHorizontalRadius->setValue(r); ui->sldHorizontalRadius->blockSignals(false); if (ui->btnAspect->keepAspectRatio()) { ui->sldVerticalRadius->blockSignals(true); ui->sldVerticalRadius->setValue(r); ui->sldVerticalRadius->blockSignals(false); } } void KisWdgEdgeDetection::verticalRadiusChanged(qreal r) { ui->sldVerticalRadius->blockSignals(true); ui->sldVerticalRadius->setValue(r); ui->sldVerticalRadius->blockSignals(false); if (ui->btnAspect->keepAspectRatio()) { ui->sldHorizontalRadius->blockSignals(true); ui->sldHorizontalRadius->setValue(r); ui->sldHorizontalRadius->blockSignals(false); } } +void KisWdgEdgeDetection::maxThresholdChanged(int val) +{ + if (ui->sldMinThreshold->value() > val) { + ui->sldMinThreshold->setValue(val); + } +} + +void KisWdgEdgeDetection::minThresholdChanged(int val) +{ + if (ui->sldMaxThreshold->value() < val) { + ui->sldMaxThreshold->setValue(val); + } +} + +void KisWdgEdgeDetection::detectionTypeChanged(int index) +{ + bool val = false; + if (index != 4) { + val = true; + } + + ui->sldMaxThreshold->setEnabled(val); + ui->sldMinThreshold->setEnabled(val); +} + void KisWdgEdgeDetection::aspectLockChanged(bool v) { if (v) { - ui->sldVerticalRadius->setValue( ui->sldHorizontalRadius->value() ); + ui->sldVerticalRadius->setValue(ui->sldHorizontalRadius->value()); } } diff --git a/plugins/filters/edgedetection/kis_wdg_edge_detection.h b/plugins/filters/edgedetection/kis_wdg_edge_detection.h index c26d394478..7ae513781b 100644 --- a/plugins/filters/edgedetection/kis_wdg_edge_detection.h +++ b/plugins/filters/edgedetection/kis_wdg_edge_detection.h @@ -1,52 +1,54 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * 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. */ #ifndef KIS_WDG_EDGE_DETECTION_H #define KIS_WDG_EDGE_DETECTION_H +#include "ui_wdg_edge_detection.h" #include #include -#include "ui_wdg_edge_detection.h" class Ui_WidgetEdgeDetection; - class KisWdgEdgeDetection : public KisConfigWidget { Q_OBJECT -public: - explicit KisWdgEdgeDetection(QWidget *parent); + public: + explicit KisWdgEdgeDetection(QWidget* parent); ~KisWdgEdgeDetection(); KisPropertiesConfigurationSP configuration() const override; void setConfiguration(const KisPropertiesConfigurationSP config) override; -private: - Ui_WidgetEdgeDetection *ui; + private: + Ui_WidgetEdgeDetection* ui; QStringList m_types; QStringList m_types_translatable; QStringList m_output; QStringList m_output_translatable; -private Q_SLOTS: + private Q_SLOTS: void horizontalRadiusChanged(qreal r); void verticalRadiusChanged(qreal r); void aspectLockChanged(bool v); + void maxThresholdChanged(int val); + void minThresholdChanged(int val); + void detectionTypeChanged(int index); }; #endif // KIS_WDG_EDGE_DETECTION_H diff --git a/plugins/filters/edgedetection/wdg_edge_detection.ui b/plugins/filters/edgedetection/wdg_edge_detection.ui index e4d0fb62ec..57c7706673 100644 --- a/plugins/filters/edgedetection/wdg_edge_detection.ui +++ b/plugins/filters/edgedetection/wdg_edge_detection.ui @@ -1,121 +1,133 @@ WidgetEdgeDetection 0 0 400 300 - - - - Output decides what type of information you want from the lines. - - - - - - - The formula decides how the kernel is created, so different formulas choose different values in the kernel, and thus give subtly different results. - - - + + + + Output: - - - - + Qt::Vertical 20 40 - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + This will take a desaturated result and use it to determine what is transparent. This is useful for creating edge and fringe effects. Apply result to alpha channel Formula: - + + + + - - - - Output: + + + + Output decides what type of information you want from the lines. - + 0 0 5 20 - - - - Qt::Horizontal + + + + The formula decides how the kernel is created, so different formulas choose different values in the kernel, and thus give subtly different results. - - - 40 - 20 - + + - + + + + KoAspectButton QWidget
KoAspectButton.h
1
KisDoubleSliderSpinBox QWidget
kis_slider_spin_box.h
1
+ + KisSliderSpinBox + QWidget +
kis_slider_spin_box.h
+ 1 +