From c57b27e1e405c58e1c16253c954e892d58152a28 Mon Sep 17 00:00:00 2001 From: Zhou Chao Date: Wed, 9 Sep 2015 14:46:20 +0800 Subject: [PATCH] Add rolling guidance filter --- .../include/opencv2/ximgproc/edge_filter.hpp | 32 +++ .../perf/perf_rolling_guidance_filter.cpp | 83 +++++++ .../ximgproc/src/rolling_guidance_filter.cpp | 84 +++++++ .../test/test_rolling_guidance_filter.cpp | 215 ++++++++++++++++++ 4 files changed, 414 insertions(+) create mode 100644 modules/ximgproc/perf/perf_rolling_guidance_filter.cpp create mode 100644 modules/ximgproc/src/rolling_guidance_filter.cpp create mode 100644 modules/ximgproc/test/test_rolling_guidance_filter.cpp diff --git a/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp b/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp index 60be77765..c3af71261 100644 --- a/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp +++ b/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp @@ -319,6 +319,38 @@ void jointBilateralFilter(InputArray joint, InputArray src, OutputArray dst, int ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// +/** @brief Applies the rolling guidance filter to an image. + +@param src Source 8-bit or floating-point, 1-channel or 3-channel image. + +@param dst Destination image of the same size and type as src. + +@param d Diameter of each pixel neighborhood that is used during filtering. If it is non-positive, +it is computed from sigmaSpace . + +@param sigmaColor Filter sigma in the color space. A larger value of the parameter means that +farther colors within the pixel neighborhood (see sigmaSpace ) will be mixed together, resulting in +larger areas of semi-equal color. + +@param sigmaSpace Filter sigma in the coordinate space. A larger value of the parameter means that +farther pixels will influence each other as long as their colors are close enough (see sigmaColor ). +When d\>0 , it specifies the neighborhood size regardless of sigmaSpace . Otherwise, d is +proportional to sigmaSpace . + +@param numOfIter Number of iterations of joint edge-preserving filtering applied on the source image. + +@param borderType + +@note rollingGuidanceFilter uses jointBilateralFilter as the edge-preserving filter. + +@sa jointBilateralFilter, bilateralFilter, amFilter +*/ +CV_EXPORTS_W +void rollingGuidanceFilter(InputArray src, OutputArray dst, int d = -1, double sigmaColor = 25, double sigmaSpace = 3, int numOfIter = 4, int borderType = BORDER_DEFAULT); + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + /** @brief Interface for implementations of Fast Global Smoother filter. diff --git a/modules/ximgproc/perf/perf_rolling_guidance_filter.cpp b/modules/ximgproc/perf/perf_rolling_guidance_filter.cpp new file mode 100644 index 000000000..a0bc3d400 --- /dev/null +++ b/modules/ximgproc/perf/perf_rolling_guidance_filter.cpp @@ -0,0 +1,83 @@ +/* + * By downloading, copying, installing or using the software you agree to this license. + * If you do not agree to this license, do not download, install, + * copy or use the software. + * + * + * License Agreement + * For Open Source Computer Vision Library + * (3 - clause BSD License) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met : + * + * *Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and / or other materials provided with the distribution. + * + * * Neither the names of the copyright holders nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * This software is provided by the copyright holders and contributors "as is" and + * any express or implied warranties, including, but not limited to, the implied + * warranties of merchantability and fitness for a particular purpose are disclaimed. + * In no event shall copyright holders or contributors be liable for any direct, + * indirect, incidental, special, exemplary, or consequential damages + * (including, but not limited to, procurement of substitute goods or services; + * loss of use, data, or profits; or business interruption) however caused + * and on any theory of liability, whether in contract, strict liability, + * or tort(including negligence or otherwise) arising in any way out of + * the use of this software, even if advised of the possibility of such damage. + */ + +#include "perf_precomp.hpp" + +namespace cvtest +{ + +using std::tr1::tuple; +using std::tr1::get; +using namespace perf; +using namespace testing; +using namespace cv; +using namespace cv::ximgproc; + +typedef tuple RGFTestParam; +typedef TestBaseWithParam RollingGuidanceFilterTest; + +PERF_TEST_P(RollingGuidanceFilterTest, perf, + Combine( + Values(2.0, 4.0, 6.0, 10.0), + SZ_TYPICAL, + Values(CV_8U, CV_32F), + Values(1, 3)) +) +{ + RGFTestParam params = GetParam(); + double sigmaS = get<0>(params); + Size sz = get<1>(params); + int depth = get<2>(params); + int srcCn = get<3>(params); + + Mat src(sz, CV_MAKE_TYPE(depth, srcCn)); + Mat dst(sz, src.type()); + + cv::setNumThreads(cv::getNumberOfCPUs()); + declare.in(src, WARMUP_RNG).out(dst).tbb_threads(cv::getNumberOfCPUs()); + + RNG rnd(cvRound(10*sigmaS) + sz.height + depth + srcCn); + double sigmaC = rnd.uniform(1.0, 255.0); + int iterNum = int(rnd.uniform(1.0, 5.0)); + + TEST_CYCLE_N(1) + { + rollingGuidanceFilter(src, dst, -1, sigmaC, sigmaS, iterNum); + } + + SANITY_CHECK_NOTHING(); +} +} diff --git a/modules/ximgproc/src/rolling_guidance_filter.cpp b/modules/ximgproc/src/rolling_guidance_filter.cpp new file mode 100644 index 000000000..679b296e0 --- /dev/null +++ b/modules/ximgproc/src/rolling_guidance_filter.cpp @@ -0,0 +1,84 @@ +/* + * By downloading, copying, installing or using the software you agree to this license. + * If you do not agree to this license, do not download, install, + * copy or use the software. + * + * + * License Agreement + * For Open Source Computer Vision Library + * (3 - clause BSD License) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met : + * + * *Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and / or other materials provided with the distribution. + * + * * Neither the names of the copyright holders nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * This software is provided by the copyright holders and contributors "as is" and + * any express or implied warranties, including, but not limited to, the implied + * warranties of merchantability and fitness for a particular purpose are disclaimed. + * In no event shall copyright holders or contributors be liable for any direct, + * indirect, incidental, special, exemplary, or consequential damages + * (including, but not limited to, procurement of substitute goods or services; + * loss of use, data, or profits; or business interruption) however caused + * and on any theory of liability, whether in contract, strict liability, + * or tort(including negligence or otherwise) arising in any way out of + * the use of this software, even if advised of the possibility of such damage. + */ + +#include "precomp.hpp" +#include +#include + +namespace cv +{ +namespace ximgproc +{ + void rollingGuidanceFilter(InputArray src_, OutputArray dst_, int d, + double sigmaColor, double sigmaSpace, int numOfIter, int borderType) + { + CV_Assert(!src_.empty()); + + Mat guidance = src_.getMat(); + Mat src = src_.getMat(); + + CV_Assert(src.size() == guidance.size()); + CV_Assert(src.depth() == guidance.depth() && (src.depth() == CV_8U || src.depth() == CV_32F) ); + + if (sigmaColor <= 0) + sigmaColor = 1; + if (sigmaSpace <= 0) + sigmaSpace = 1; + + dst_.create(src.size(), src.type()); + Mat dst = dst_.getMat(); + + if (src.data == guidance.data) + guidance = guidance.clone(); + if (dst.data == src.data) + src = src.clone(); + + int srcCnNum = src.channels(); + + if (srcCnNum == 1 || srcCnNum == 3) + { + while(numOfIter--){ + jointBilateralFilter(guidance, src, guidance, d, sigmaColor, sigmaSpace, borderType); + } + guidance.copyTo(dst_); + } + else + { + CV_Error(Error::BadNumChannels, "Unsupported number of channels"); + } + } +} +} diff --git a/modules/ximgproc/test/test_rolling_guidance_filter.cpp b/modules/ximgproc/test/test_rolling_guidance_filter.cpp new file mode 100644 index 000000000..604f8514c --- /dev/null +++ b/modules/ximgproc/test/test_rolling_guidance_filter.cpp @@ -0,0 +1,215 @@ +/* + * By downloading, copying, installing or using the software you agree to this license. + * If you do not agree to this license, do not download, install, + * copy or use the software. + * + * + * License Agreement + * For Open Source Computer Vision Library + * (3 - clause BSD License) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met : + * + * *Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and / or other materials provided with the distribution. + * + * * Neither the names of the copyright holders nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * This software is provided by the copyright holders and contributors "as is" and + * any express or implied warranties, including, but not limited to, the implied + * warranties of merchantability and fitness for a particular purpose are disclaimed. + * In no event shall copyright holders or contributors be liable for any direct, + * indirect, incidental, special, exemplary, or consequential damages + * (including, but not limited to, procurement of substitute goods or services; + * loss of use, data, or profits; or business interruption) however caused + * and on any theory of liability, whether in contract, strict liability, + * or tort(including negligence or otherwise) arising in any way out of + * the use of this software, even if advised of the possibility of such damage. + */ + +#include "test_precomp.hpp" + +namespace cvtest +{ + +using namespace std; +using namespace std::tr1; +using namespace testing; +using namespace perf; +using namespace cv; +using namespace cv::ximgproc; + +static std::string getOpenCVExtraDir() +{ + return cvtest::TS::ptr()->get_data_path(); +} + +static void checkSimilarity(InputArray src, InputArray ref) +{ + double normInf = cvtest::norm(src, ref, NORM_INF); + double normL2 = cvtest::norm(src, ref, NORM_L2) / (src.total()*src.channels()); + + EXPECT_LE(normInf, 1.0); + EXPECT_LE(normL2, 1.0 / 16); +} + +static Mat convertTypeAndSize(Mat src, int dstType, Size dstSize) +{ + Mat dst; + int srcCnNum = src.channels(); + int dstCnNum = CV_MAT_CN(dstType); + + if (srcCnNum == dstCnNum) + { + src.copyTo(dst); + } + else if (srcCnNum == 3 && dstCnNum == 1) + { + cvtColor(src, dst, COLOR_BGR2GRAY); + } + else if (srcCnNum == 1 && dstCnNum == 3) + { + cvtColor(src, dst, COLOR_GRAY2BGR); + } + else + { + CV_Error(Error::BadNumChannels, "Bad num channels in src"); + } + + dst.convertTo(dst, dstType); + resize(dst, dst, dstSize); + + return dst; +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +typedef tuple RGFParams; +typedef TestWithParam RollingGuidanceFilterTest; + +TEST_P(RollingGuidanceFilterTest, SplatSurfaceAccuracy) +{ + RGFParams params = GetParam(); + double sigmaS = get<0>(params); + int depth = get<1>(params); + int srcCn = get<2>(params); + + RNG rnd(0); + + Size sz(rnd.uniform(512,1024), rnd.uniform(512,1024)); + + for (int i = 0; i < 5; i++) + { + Scalar surfaceValue; + rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255); + Mat src(sz, CV_MAKE_TYPE(depth, srcCn), surfaceValue); + + double sigmaC = rnd.uniform(1.0, 255.0); + int iterNum = int(rnd.uniform(1.0, 5.0)); + + Mat res; + rollingGuidanceFilter(src, res, -1, sigmaC, sigmaS, iterNum); + + double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels(); + EXPECT_LE(normL1, 1.0/64); + } +} + +TEST_P(RollingGuidanceFilterTest, MultiThreadReproducibility) +{ + if (cv::getNumberOfCPUs() == 1) + return; + + RGFParams params = GetParam(); + double sigmaS = get<0>(params); + int depth = get<1>(params); + int srcCn = get<2>(params); + + double MAX_DIF = 1.0; + double MAX_MEAN_DIF = 1.0 / 64.0; + int loopsCount = 2; + RNG rnd(1); + + Size sz(rnd.uniform(512,1024), rnd.uniform(512,1024)); + + Mat src(sz,CV_MAKE_TYPE(depth, srcCn)); + if(src.depth()==CV_8U) + randu(src, 0, 255); + else if(src.depth()==CV_16S) + randu(src, -32767, 32767); + else + randu(src, -100000.0f, 100000.0f); + + for (int iter = 0; iter <= loopsCount; iter++) + { + int iterNum = int(rnd.uniform(1.0, 5.0)); + double sigmaC = rnd.uniform(1.0, 255.0); + + cv::setNumThreads(cv::getNumberOfCPUs()); + Mat resMultiThread; + rollingGuidanceFilter(src, resMultiThread, -1, sigmaC, sigmaS, iterNum); + + cv::setNumThreads(1); + Mat resSingleThread; + rollingGuidanceFilter(src, resSingleThread, -1, sigmaC, sigmaS, iterNum); + + EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_INF), MAX_DIF); + EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_L1), MAX_MEAN_DIF*src.total()*src.channels()); + } +} + +INSTANTIATE_TEST_CASE_P(TypicalSet1, RollingGuidanceFilterTest, + Combine( + Values(2.0, 5.0), + Values(CV_8U, CV_32F), + Values(1, 3) + ) +); + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +typedef tuple RGFBFParam; +typedef TestWithParam RollingGuidanceFilterTest_BilateralRef; + +TEST_P(RollingGuidanceFilterTest_BilateralRef, Accuracy) +{ + RGFBFParam params = GetParam(); + double sigmaS = get<0>(params); + string srcPath = get<1>(params); + int srcType = get<2>(params); + + Mat src = imread(getOpenCVExtraDir() + srcPath); + ASSERT_TRUE(!src.empty()); + src = convertTypeAndSize(src, srcType, src.size()); + + RNG rnd(0); + double sigmaC = rnd.uniform(0.0, 255.0); + + cv::setNumThreads(cv::getNumberOfCPUs()); + + Mat resRef; + bilateralFilter(src, resRef, 0, sigmaC, sigmaS); + + Mat res, joint = src.clone(); + rollingGuidanceFilter(src, res, 0, sigmaC, sigmaS, 1); + + checkSimilarity(res, resRef); +} + +INSTANTIATE_TEST_CASE_P(TypicalSet2, RollingGuidanceFilterTest_BilateralRef, + Combine( + Values(4.0, 6.0, 8.0), + Values("/cv/shared/pic2.png", "/cv/shared/lena.png", "cv/shared/box_in_scene.png"), + Values(CV_8UC1, CV_8UC3, CV_32FC1, CV_32FC3) + ) +); +}