diff --git a/libs/image/kis_edge_detection_kernel.cpp b/libs/image/kis_edge_detection_kernel.cpp index 7872414e08..073f7efe5c 100644 --- a/libs/image/kis_edge_detection_kernel.cpp +++ b/libs/image/kis_edge_detection_kernel.cpp @@ -1,651 +1,667 @@ /* * 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 #include #include #include #include #include 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) { 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) { 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, qreal xRadius, qreal yRadius, KisEdgeDetectionKernel::FilterType type, 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, 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 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()) { 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]; } 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 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, srcTopLeft - QPoint(0, ceil(center)), srcTopLeft - QPoint(0, ceil(center)), 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, 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); + Eigen::Matrix horizontalMatrix; + horizontalMatrix << -1, 0, 1, + -2, 0, 2, + -1, 0, 1; - qreal horizontalCenter = qreal(kernelHorizLeftRight->width()) / 2.0; - qreal verticalCenter = qreal(kernelVerticalTopBottom->height()) / 2.0; + auto verticalMatrix = horizontalMatrix.transpose(); + + auto kernelHorizLeftRight = KisConvolutionKernel::fromMatrix(horizontalMatrix, 0.5, 1); + auto kernelVerticalTopBottom = KisConvolutionKernel::fromMatrix(verticalMatrix, 0.5, 1); 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)), + srcTopLeft - QPoint(0, 1), + srcTopLeft - QPoint(0, 1), + rect.size() + QSize(0, 2), 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)), + srcTopLeft - QPoint(0, 1), + srcTopLeft - QPoint(0, 1), + rect.size() + QSize(0, 2), 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))); + finalNorm[0] = 2 * hypot(xNormalised[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()); + KisRandomConstAccessorSP gy_iter = y_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()); + device->colorSpace()->normalisedChannelsValue(gx_iter->rawDataConst(), yNormalised); + device->colorSpace()->normalisedChannelsValue(gy_iter->rawDataConst(), xNormalised); + device->colorSpace()->normalisedChannelsValue(randomSobel->rawDataConst(), finalNorm); + + const float direction = static_cast((fmod(atan2(yNormalised[0] - 0.5, xNormalised[0] - 0.5) + M_PI, M_PI)/M_PI) * 8); if (direction <= 1 || direction > 7) { randomSobel->moveTo(x + 1, y); - firstIntensity = *randomSobel->rawData(); + device->colorSpace()->normalisedChannelsValue(gx_iter->rawDataConst(), yNormalised); + firstIntensity = yNormalised[0]; + randomSobel->moveTo(x - 1, y); - secondIntensity = *randomSobel->rawData(); + device->colorSpace()->normalisedChannelsValue(gy_iter->rawDataConst(), xNormalised); + secondIntensity = xNormalised[0]; } - if (direction > 1 || direction <= 3) { + if (direction > 1 && direction <= 3) { randomSobel->moveTo(x + 1, y + 1); - firstIntensity = *randomSobel->rawData(); + device->colorSpace()->normalisedChannelsValue(gx_iter->rawDataConst(), yNormalised); + firstIntensity = yNormalised[0]; + randomSobel->moveTo(x - 1, y - 1); - secondIntensity = *randomSobel->rawData(); + device->colorSpace()->normalisedChannelsValue(gy_iter->rawDataConst(), xNormalised); + secondIntensity = xNormalised[0]; } - if (direction > 3 || direction <= 5) { + if (direction > 3 && direction <= 5) { randomSobel->moveTo(x, y + 1); - firstIntensity = *randomSobel->rawData(); + device->colorSpace()->normalisedChannelsValue(gx_iter->rawDataConst(), yNormalised); + firstIntensity = yNormalised[0]; + randomSobel->moveTo(x, y - 1); - secondIntensity = *randomSobel->rawData(); + device->colorSpace()->normalisedChannelsValue(gy_iter->rawDataConst(), xNormalised); + secondIntensity = xNormalised[0]; } - if (direction > 5 || direction <= 7) { + if (direction > 5 && direction <= 7) { randomSobel->moveTo(x + 1, y - 1); - firstIntensity = *randomSobel->rawData(); + device->colorSpace()->normalisedChannelsValue(gx_iter->rawDataConst(), yNormalised); + firstIntensity = yNormalised[0]; + randomSobel->moveTo(x - 1, y + 1); - secondIntensity = *randomSobel->rawData(); + device->colorSpace()->normalisedChannelsValue(gy_iter->rawDataConst(), xNormalised); + secondIntensity = xNormalised[0]; } randomSobel->moveTo(x, y); - if (2 * current <= (firstIntensity + secondIntensity)) { + if (finalNorm[0] < firstIntensity || finalNorm[0] < secondIntensity) { quint8* temp = new quint8(); - finalNorm[0] = 0.0; + finalNorm[0] = 0; device->colorSpace()->fromNormalisedChannelsValue(temp, finalNorm); memcpy(randomSobel->rawData(), temp, device->pixelSize()); } } } 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, qreal xRadius, qreal yRadius, KisEdgeDetectionKernel::FilterType type, int channelToConvert, QVector channelOrder, QVector channelFlip, 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]); 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, srcTopLeft - QPoint(ceil(horizontalCenter), 0), srcTopLeft - QPoint(ceil(horizontalCenter), 0), rect.size() + QSize(2 * ceil(horizontalCenter), 0), BORDER_REPEAT); KisConvolutionPainter verticalPainterTB(x_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 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()) { device->colorSpace()->normalisedChannelsValue(yItterator.rawData(), yNormalised); device->colorSpace()->normalisedChannelsValue(xItterator.rawData(), xNormalised); qreal z = 1.0; if (channelFlip[2]==true){ z=-1.0; } 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; } finalNorm[alphaPos]= 1.0; quint8* pixel = finalIt.rawData(); device->colorSpace()->fromNormalisedChannelsValue(pixel, finalNorm); memcpy(finalIt.rawData(), pixel, pixelSize); } } diff --git a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp index 962bf437fb..4e8e8829b8 100644 --- a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp +++ b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp @@ -1,170 +1,177 @@ /* * 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 #include #include #include "kis_lod_transform.h" #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KritaEdgeDetectionFilterFactory, "kritaedgedetection.json", registerPlugin();) KritaEdgeDetectionFilter::KritaEdgeDetectionFilter(QObject *parent, const QVariantList &) : QObject(parent) { KisFilterRegistry::instance()->add(KisFilterSP(new KisEdgeDetectionFilter())); } KritaEdgeDetectionFilter::~KritaEdgeDetectionFilter() { } 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 { Q_ASSERT(device != 0); KisFilterConfigurationSP configuration = config ? config : new KisFilterConfiguration(id().id(), 1); KisLodTransformScalar t(device); QVariant value; configuration->getProperty("horizRadius", value); float horizontalRadius = t.scale(value.toFloat()); configuration->getProperty("vertRadius", value); float verticalRadius = t.scale(value.toFloat()); QBitArray channelFlags; if (configuration) { channelFlags = configuration->channelFlags(); } 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; } else 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); config->setProperty("max-threshold", 200); config->setProperty("min-threshold", 100); return config; } 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 { KisLodTransformScalar t(lod); + if(_config->getProperty("type") == "canny"){ + return kisGrowRect(rect, 4); + } 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; return rect.adjusted(-halfWidth * 2, -halfHeight * 2, halfWidth * 2, halfHeight * 2); } QRect KisEdgeDetectionFilter::changedRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const { KisLodTransformScalar t(lod); QVariant value; + if(_config->getProperty("type") == "canny"){ + return kisGrowRect(rect, 4); + } + 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); } #include "kis_edge_detection_filter.moc"