diff --git a/libs/image/kis_edge_detection_kernel.h b/libs/image/kis_edge_detection_kernel.h index f5c4d6b7d2..9b1f44e9f0 100644 --- a/libs/image/kis_edge_detection_kernel.h +++ b/libs/image/kis_edge_detection_kernel.h @@ -1,131 +1,139 @@ /* * 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 class QRect; class KRITAIMAGE_EXPORT KisEdgeDetectionKernel { 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 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); /** * @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 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 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); + + static void applyCannyEdgeDetection(KisPaintDeviceSP device, + const QRect& rect, + qreal xRadius, qreal yRadius, + const QBitArray &channelFlags, + KoUpdater *progressUpdater, + bool writeToAlpha = false); + /** * @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); }; #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 0564f73efc..362dae39da 100644 --- a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp +++ b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp @@ -1,156 +1,163 @@ /* * 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()); + qreal horizontalRadius = t.scale(value.toFloat()); configuration->getProperty("vertRadius", value); - float verticalRadius = t.scale(value.toFloat()); + qreal verticalRadius = t.scale(value.toFloat()); QBitArray channelFlags; if (configuration) { channelFlags = configuration->channelFlags(); } + if(config->getString("type") == "canny") { + return; + } + KisEdgeDetectionKernel::FilterType type = KisEdgeDetectionKernel::SobelVector; + if (config->getString("type") == "prewitt") { type = KisEdgeDetectionKernel::Prewit; - } else if (config->getString("type") == "simple") { + } + + 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 { Q_UNUSED(dev); return new KisWdgEdgeDetection(parent); } 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; 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; 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" diff --git a/plugins/filters/tests/kis_canny_filter_test.cc b/plugins/filters/tests/kis_canny_filter_test.cc index e8671b9586..e15ec99249 100644 --- a/plugins/filters/tests/kis_canny_filter_test.cc +++ b/plugins/filters/tests/kis_canny_filter_test.cc @@ -1,27 +1,32 @@ #include "kis_canny_filter_test.h" -#include "kis_edge_detection_kernel.h" +#include "filter/kis_filter.h" +#include "filter/kis_filter_configuration.h" +#include "filter/kis_filter_registry.h" #include #include #include inline KisPaintDeviceSP loadTestImage(const QString &name) { QImage image(TestUtil::fetchDataFileLazy(name)); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(image, nullptr); return dev; } void KisCannyFilterTest::testCannyFilter() { KisPaintDeviceSP dev = loadTestImage("carrot.png"); const QRect rect = dev->exactBounds(); - //KisEdgeDetectionKernel::applyCannyEdgeDetection() + KisFilterSP filter = KisFilterRegistry::instance()->value("edge detection"); + auto conf = filter->defaultConfiguration(); + conf->setProperty("type", "canny"); + filter->process(dev, rect, conf); QImage img = dev->convertToQImage(nullptr, rect); img.save("canny_filter_tested.png"); } QTEST_MAIN(KisCannyFilterTest)