From 88379486c33787fe1696ab3bb86a1936ec4b4b5c Mon Sep 17 00:00:00 2001 From: Zhou Chao Date: Mon, 14 Sep 2015 19:22:45 +0800 Subject: [PATCH] Add weighted median filter --- modules/ximgproc/doc/ximgproc.bib | 18 + modules/ximgproc/include/opencv2/ximgproc.hpp | 1 + .../include/opencv2/ximgproc/edge_filter.hpp | 2 + .../ximgproc/weighted_median_filter.hpp | 95 +++ .../perf/perf_weighted_median_filter.cpp | 88 +++ .../ximgproc/src/weighted_median_filter.cpp | 723 ++++++++++++++++++ .../test/test_weighted_median_filter.cpp | 107 +++ 7 files changed, 1034 insertions(+) create mode 100644 modules/ximgproc/include/opencv2/ximgproc/weighted_median_filter.hpp create mode 100644 modules/ximgproc/perf/perf_weighted_median_filter.cpp create mode 100644 modules/ximgproc/src/weighted_median_filter.cpp create mode 100644 modules/ximgproc/test/test_weighted_median_filter.cpp diff --git a/modules/ximgproc/doc/ximgproc.bib b/modules/ximgproc/doc/ximgproc.bib index a03cb4751..7b5e232bb 100644 --- a/modules/ximgproc/doc/ximgproc.bib +++ b/modules/ximgproc/doc/ximgproc.bib @@ -136,3 +136,21 @@ month = {June}, year = {2015} } + +@incollection{zhang2014rolling, + title={Rolling guidance filter}, + author={Zhang, Qi and Shen, Xiaoyong and Xu, Li and Jia, Jiaya}, + booktitle={Computer Vision--ECCV 2014}, + pages={815--830}, + year={2014}, + publisher={Springer} +} + +@inproceedings{zhang2014100+, + title={100+ times faster weighted median filter (WMF)}, + author={Zhang, Qi and Xu, Li and Jia, Jiaya}, + booktitle={Computer Vision and Pattern Recognition (CVPR), 2014 IEEE Conference on}, + pages={2830--2837}, + year={2014}, + organization={IEEE} +} diff --git a/modules/ximgproc/include/opencv2/ximgproc.hpp b/modules/ximgproc/include/opencv2/ximgproc.hpp index c96a257fe..17af6c35c 100644 --- a/modules/ximgproc/include/opencv2/ximgproc.hpp +++ b/modules/ximgproc/include/opencv2/ximgproc.hpp @@ -45,6 +45,7 @@ #include "ximgproc/segmentation.hpp" #include "ximgproc/fast_hough_transform.hpp" #include "ximgproc/estimated_covariance.hpp" +#include "ximgproc/weighted_median_filter.hpp" #include "ximgproc/slic.hpp" #include "ximgproc/lsc.hpp" diff --git a/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp b/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp index f87138abe..9b722fa75 100644 --- a/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp +++ b/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp @@ -321,6 +321,8 @@ void jointBilateralFilter(InputArray joint, InputArray src, OutputArray dst, int /** @brief Applies the rolling guidance filter to an image. +For more details, please see @cite zhang2014rolling + @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. diff --git a/modules/ximgproc/include/opencv2/ximgproc/weighted_median_filter.hpp b/modules/ximgproc/include/opencv2/ximgproc/weighted_median_filter.hpp new file mode 100644 index 000000000..30a169cd8 --- /dev/null +++ b/modules/ximgproc/include/opencv2/ximgproc/weighted_median_filter.hpp @@ -0,0 +1,95 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// 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 +// +// Copyright (C) 2015, The Chinese University of Hong Kong, all rights reserved. +// +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 the Intel Corporation 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. +// +//M*/ + +#ifndef __OPENCV_WEIGHTED_MEDIAN_FILTER_HPP__ +#define __OPENCV_WEIGHTED_MEDIAN_FILTER_HPP__ +#ifdef __cplusplus + +/** +* @file +* @date Sept 9, 2015 +* @author Zhou Chao +*/ + +#include +#include + +namespace cv +{ +namespace ximgproc +{ + +/** +* @brief Specifies weight types of weighted median filter. +*/ +enum WMFWeightType +{ + WMF_EXP, //!< \f$exp(-|I1-I2|^2/(2*sigma^2))\f$ + WMF_IV1, //!< \f$(|I1-I2|+sigma)^-1\f$ + WMF_IV2, //!< \f$(|I1-I2|^2+sigma^2)^-1\f$ + WMF_COS, //!< \f$dot(I1,I2)/(|I1|*|I2|)\f$ + WMF_JAC, //!< \f$(min(r1,r2)+min(g1,g2)+min(b1,b2))/(max(r1,r2)+max(g1,g2)+max(b1,b2))\f$ + WMF_OFF //!< unweighted +}; + +/** +* @brief Applies weighted median filter to an image. +* +* For more details about this implementation, please see @cite zhang2014100+ +* +* @param joint Joint 8-bit, 1-channel or 3-channel image. +* @param src Source 8-bit or floating-point, 1-channel or 3-channel image. +* @param dst Destination image. +* @param r Radius of filtering kernel, should be a positive integer. +* @param sigma Filter range standard deviation for the joint image. +* @param weightType weightType The type of weight definition, see WMFWeightType +* @param mask A 0-1 mask that has the same size with I. This mask is used to ignore the effect of some pixels. If the pixel value on mask is 0, +* the pixel will be ignored when maintaining the joint-histogram. This is useful for applications like optical flow occlusion handling. +* +* @sa medianBlur, jointBilateralFilter +*/ +CV_EXPORTS void weightedMedianFilter(InputArray joint, InputArray src, OutputArray dst, int r, double sigma=25.5, WMFWeightType weightType=WMF_EXP, Mat mask=Mat()); +} +} + +#endif +#endif diff --git a/modules/ximgproc/perf/perf_weighted_median_filter.cpp b/modules/ximgproc/perf/perf_weighted_median_filter.cpp new file mode 100644 index 000000000..b8acfa9ab --- /dev/null +++ b/modules/ximgproc/perf/perf_weighted_median_filter.cpp @@ -0,0 +1,88 @@ +/* + * 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 WMFTestParam; + typedef TestBaseWithParam WeightedMedianFilterTest; + + PERF_TEST_P(WeightedMedianFilterTest, perf, + Combine( + Values(szODD, szQVGA), + Values(CV_8U, CV_32F), + Values(1, 3), + Values(1, 3), + Values(3, 5), + Values(WMF_EXP, WMF_COS)) + ) + { + RNG rnd(1); + + WMFTestParam params = GetParam(); + + double sigma = rnd.uniform(20.0, 30.0); + Size sz = get<0>(params); + int srcDepth = get<1>(params); + int jCn = get<2>(params); + int srcCn = get<3>(params); + int r = get<4>(params); + WMFWeightType weightType = get<5>(params); + + Mat joint(sz, CV_MAKE_TYPE(CV_8U, jCn)); + Mat src(sz, CV_MAKE_TYPE(srcDepth, srcCn)); + Mat dst(sz, src.type()); + + cv::setNumThreads(cv::getNumberOfCPUs()); + declare.in(joint, src, WARMUP_RNG).out(dst).tbb_threads(cv::getNumberOfCPUs()); + + TEST_CYCLE_N(1) + { + weightedMedianFilter(joint, src, dst, r, sigma, weightType); + } + + SANITY_CHECK_NOTHING(); + } + } diff --git a/modules/ximgproc/src/weighted_median_filter.cpp b/modules/ximgproc/src/weighted_median_filter.cpp new file mode 100644 index 000000000..0d03bc1d1 --- /dev/null +++ b/modules/ximgproc/src/weighted_median_filter.cpp @@ -0,0 +1,723 @@ +/* + * 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 + +using namespace std; +using namespace cv; + +namespace{ + +using namespace cv::ximgproc; + +/***************************************************************/ +/* Function: from32FTo32S + * Description: adaptive quantization for changing a floating-point 1D image to integer image. + * The adaptive quantization strategy is based on binary search, which searches an + * upper bound of quantization error. + * The function also return a mapping between quantized value (32F) and quantized index (32S). + * The mapping is used to convert integer image back to floating-point image after filtering. + ***************************************************************/ +void from32FTo32S(Mat &img, Mat &outImg, int nI, float *mapping) +{ + int rows = img.rows, cols = img.cols; + int alls = rows * cols; + + float *imgPtr = img.ptr(); + typedef pair pairFI; + pairFI *data = (pairFI *)malloc(alls*sizeof(pairFI)); + + // Sort all pixels of the image by ascending order of pixel value + for(int i=0;i th) + { + float m = (r+l)*0.5f; + bool suc = true; + float base = (float)minVal; + int cnt=0; + for(int i=0;ibase+m) + { + cnt++; + base = data[i].first; + if(cnt==nI) + { + suc = false; + break; + } + } + } + if(suc)r=m; + else l=m; + } + + Mat retImg(img.size(),CV_32SC1); + int *retImgPtr = retImg.ptr(); + + // In the sorted list, divide pixel values into clusters according to the minimum error bound + // Quantize each value to the median of its cluster + // Also record the mapping of quantized value and quantized index. + float base = (float)minVal; + int baseI = 0; + int cnt = 0; + for(int i=0;i<=alls;i++) + { + if(i==alls || data[i].first>base+r) + { + mapping[cnt] = data[(baseI+i-1)>>1].first; //median + if(i==alls)break; + cnt++; + base = data[i].first; + baseI = i; + } + retImgPtr[data[i].second] = cnt; + } + + free(data); + + //end of the function + outImg = retImg; +} + +/***************************************************************/ +/* Function: from32STo32F + * Description: convert the quantization index image back to the floating-point image accroding to the mapping +***************************************************************/ +void from32STo32F(Mat &img, Mat &outImg, float *mapping) +{ + Mat retImg(img.size(),CV_32F); + int rows = img.rows, cols = img.cols, alls = rows*cols; + float *retImgPtr = retImg.ptr(); + int *imgPtr = img.ptr(); + + // convert 32S index to 32F real value + for(int i=0;i64(6-bit) + const int LOW_NUM = 256>>shift; + static int hash[LOW_NUM][LOW_NUM][LOW_NUM]={{{0}}}; + + memset(hash,0,sizeof(hash)); + + // throw pixels into a 2D histogram + int candCnt = 0; + { + int lowR,lowG,lowB; + uchar *FPtr = F.ptr(); + for(int i=0,i3=0;i>shift; + lowG = FPtr[i3+1]>>shift; + lowR = FPtr[i3+2]>>shift; + + if(hash[lowB][lowG][lowR]==0) + { + candCnt++; + hash[lowB][lowG][lowR]=1; + } + } + } + + nF = min(nF, candCnt); + Mat samples(candCnt,3,CV_32F); + + //prepare for K-means + int top=0; + for(int i=0;i(top)[0] = (float)i; + samples.ptr(top)[1] = (float)j; + samples.ptr(top)[2] = (float)k; + top++; + } + } + + //do K-means + Mat labels; + Mat centers; + kmeans(samples, nF, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 0, 10000), KmeansAttempts, KMEANS_PP_CENTERS, centers ); + + //make connection (i,j,k) <-> index + top = 0; + for(int i=0;i(top)[0]; + top++; + } + } + + // generate index map + FNew = Mat(F.size(),CV_32SC1); + + int lowR,lowG,lowB; + uchar *FPtr = F.ptr(); + for(int i=0,i3=0;i>shift; + lowG = FPtr[i3+1]>>shift; + lowR = FPtr[i3+2]>>shift; + + FNew.ptr()[i] = hash[lowB][lowG][lowR]; + } + + // Compute weight map (weight between each pair of feature index) + wMap = float2D(nF,nF); + float nSigmaI = sigmaI/256.0f*LOW_NUM; + float divider = (1.0f/(2*nSigmaI*nSigmaI)); + + float *length = new float[nF]; + for(int i=0;i(i)[0]; + float a1 = centers.ptr(i)[1]; + float a2 = centers.ptr(i)[2]; + length[i] = sqrt(a0*a0+a1*a1+a2*a2); + } + + for(int i=0;i(i)[0], b0 = centers.ptr(j)[0]; + float a1 = centers.ptr(i)[1], b1 = centers.ptr(j)[1]; + float a2 = centers.ptr(i)[2], b2 = centers.ptr(j)[2]; + float diff0 = a0-b0; + float diff1 = a1-b1; + float diff2 = a2-b2; + + float val; + + switch(weightType) + { + case WMF_EXP: val = exp(-(diff0*diff0+diff1*diff1+diff2*diff2)*divider); break; + case WMF_IV1: val = 1.0f/(fabs(diff0)+fabs(diff1)+fabs(diff2)+nSigmaI); break; + case WMF_IV2: val = 1.0f / (diff0*diff0+diff1*diff1+diff2*diff2+nSigmaI*nSigmaI); break; + case WMF_COS: val = (a0*b0+a1*b1+a2*b2)/(length[i]*length[j]); break; + case WMF_JAC: val = (min(a0,b0)+min(a1,b1)+min(a2,b2))/(max(a0,b0)+max(a1,b1)+max(a2,b2)); break; + case WMF_OFF: val = 1.0f; break; + default: val = exp(-(diff0*diff0+diff1*diff1+diff2*diff2)*divider); + } + + wMap[i][j] = wMap[j][i] = val; + } + } + + delete []length; + } + //end of the function + F = FNew; +} + +Mat filterCore(Mat &I, Mat &F, float **wMap, int r=20, int nF=256, int nI=256, Mat mask=Mat()) +{ + // Check validation + assert(I.depth() == CV_32S && I.channels()==1);//input image: 32SC1 + assert(F.depth() == CV_32S && F.channels()==1);//feature image: 32SC1 + + // Configuration and declaration + int rows = I.rows, cols = I.cols; + Mat outImg = I.clone(); + + // Handle Mask + if(mask.empty()) + { + mask = Mat(I.size(),CV_8U); + mask = Scalar(1); + } + + // Allocate memory for joint-histogram and BCB + int **H = int2D(nI,nF); + int *BCB = new int[nF]; + + // Allocate links for necklace table + int **Hf = int2D(nI,nF);//forward link + int **Hb = int2D(nI,nF);//backward link + int *BCBf = new int[nF];//forward link + int *BCBb = new int[nF];//backward link + + // Column Scanning + for(int x=0;x(i); + int *FPtr = F.ptr(i); + uchar *maskPtr = mask.ptr(i); + + for(int j=downX;j<=upX;j++) + { + if(!maskPtr[j])continue; + + int fval = IPtr[j]; + int *curHist = H[fval]; + int gval = FPtr[j]; + + // Maintain necklace table of joint-histogram + if(!curHist[gval] && gval) + { + int *curHf = Hf[fval]; + int *curHb = Hb[fval]; + + int p1=0,p2=curHf[0]; + curHf[p1]=gval; + curHf[gval]=p2; + curHb[p2]=gval; + curHb[gval]=p1; + } + + curHist[gval]++; + // Maintain necklace table of BCB + updateBCB(BCB[gval],BCBf,BCBb,gval,-1); + } + } + + for(int y=0;y(y,x)[0]; + float *fPtr = wMap[curIndex]; + int &curMedianVal = medianVal; + + // Compute current balance + { + int i=0; + do + { + balanceWeight += BCB[i]*fPtr[i]; + i=BCBf[i]; + }while(i); + } + + // Move cut-point to the left + if(balanceWeight >= 0) + { + for(;balanceWeight >= 0 && curMedianVal; curMedianVal--) + { + float curWeight = 0; + int *nextHist = H[curMedianVal]; + int *nextHf = Hf[curMedianVal]; + + // Compute weight change by shift cut-point + int i=0; + do + { + curWeight += (nextHist[i]<<1)*fPtr[i]; + + // Update BCB and maintain the necklace table of BCB + updateBCB(BCB[i],BCBf,BCBb,i,-(nextHist[i]<<1)); + + i=nextHf[i]; + }while(i); + + balanceWeight -= curWeight; + } + } + // Move cut-point to the right + else if(balanceWeight < 0) + { + for(;balanceWeight < 0 && curMedianVal != nI-1; curMedianVal++) + { + float curWeight = 0; + int *nextHist = H[curMedianVal+1]; + int *nextHf = Hf[curMedianVal+1]; + + // Compute weight change by shift cut-point + int i=0; + do + { + curWeight += (nextHist[i]<<1)*fPtr[i]; + + // Update BCB and maintain the necklace table of BCB + updateBCB(BCB[i],BCBf,BCBb,i,nextHist[i]<<1); + + i=nextHf[i]; + }while(i); + balanceWeight += curWeight; + } + } + + // Weighted median is found and written to the output image + if(balanceWeight<0)outImg.ptr(y,x)[0] = curMedianVal+1; + else outImg.ptr(y,x)[0] = curMedianVal; + + // Update joint-histogram and BCB when local window is shifted. + int fval,gval,*curHist; + + // Add entering pixels into joint-histogram and BCB + int rownum = y + r + 1; + if(rownum < rows) + { + int *inputImgPtr = I.ptr(rownum); + int *guideImgPtr = F.ptr(rownum); + uchar *maskPtr = mask.ptr(rownum); + + for(int j=downX;j<=upX;j++) + { + if(!maskPtr[j])continue; + + fval = inputImgPtr[j]; + curHist = H[fval]; + gval = guideImgPtr[j]; + + // Maintain necklace table of joint-histogram + if(!curHist[gval] && gval) + { + int *curHf = Hf[fval]; + int *curHb = Hb[fval]; + + int p1=0,p2=curHf[0]; + curHf[gval]=p2; + curHb[gval]=p1; + curHf[p1]=curHb[p2]=gval; + } + + curHist[gval]++; + + // Maintain necklace table of BCB + updateBCB(BCB[gval],BCBf,BCBb,gval,((fval <= medianVal)<<1)-1); + } + } + + + // Delete leaving pixels into joint-histogram and BCB + rownum = y - r; + if(rownum >= 0) + { + int *inputImgPtr = I.ptr(rownum); + int *guideImgPtr = F.ptr(rownum); + uchar *maskPtr = mask.ptr(rownum); + + for(int j=downX;j<=upX;j++) + { + if(!maskPtr[j])continue; + + fval = inputImgPtr[j]; + curHist = H[fval]; + gval = guideImgPtr[j]; + + curHist[gval]--; + + // Maintain necklace table of joint-histogram + if(!curHist[gval] && gval) + { + int *curHf = Hf[fval]; + int *curHb = Hb[fval]; + + int p1=curHb[gval],p2=curHf[gval]; + curHf[p1]=p2; + curHb[p2]=p1; + } + + // Maintain necklace table of BCB + updateBCB(BCB[gval],BCBf,BCBb,gval,-((fval <= medianVal)<<1)+1); + } + } + } + } + + // Deallocate the memory + { + delete []BCB; + delete []BCBf; + delete []BCBb; + int2D_release(H); + int2D_release(Hf); + int2D_release(Hb); + } + + // end of the function + return outImg; +} +} + +namespace cv +{ +namespace ximgproc +{ +void weightedMedianFilter(InputArray joint, InputArray src, OutputArray dst, int r, double sigma, WMFWeightType weightType, Mat mask) +{ + CV_Assert(!src.empty()); + CV_Assert(r > 0 && sigma > 0); + + int nI = 256; + int nF = 256; + + Mat I = src.getMat(); + Mat F = joint.getMat(); + if(joint.empty()) + { + medianBlur(src, dst, r); + return; + } + + CV_Assert(I.depth() == CV_32F || I.depth() == CV_8U); + CV_Assert(F.depth() == CV_8U && (F.channels() == 1 || F.channels() == 3)); + + dst.create(src.size(), src.type()); + Mat D = dst.getMat(); + + if(D.data == F.data) + F = F.clone(); + if(D.data == I.data) + I = I.clone(); + + //Preprocess I + //OUTPUT OF THIS STEP: Is, iMap + //If I is floating point image, "adaptive quantization" is done in from32FTo32S. + //The mapping of floating value to integer value is stored in iMap (for each channel). + //"Is" stores each channel of "I". The channels are converted to CV_32S type after this step. + vector iMap(I.channels()); + vector Is; + split(I,Is); + for(int i=0;i<(int)Is.size();i++) + { + if(I.depth() == CV_32F) + { + iMap[i] = new float[nI]; + from32FTo32S(Is[i],Is[i],nI,iMap[i]); + } + else if(I.depth() == CV_8U) + { + Is[i].convertTo(Is[i],CV_32S); + } + } + + //Preprocess F + //OUTPUT OF THIS STEP: F(new), wMap + //If "F" is 3-channel image, "clustering feature image" is done in featureIndexing. + //If "F" is 1-channel image, featureIndexing only does a type-casting on "F". + //The output "F" is CV_32S type, containing indexes of feature values. + //"wMap" is a 2D array that defines the distance between each pair of feature indexes. + // wMap[i][j] is the weight between feature index "i" and "j". + float **wMap = NULL; + featureIndexing(F, wMap, nF, float(sigma), weightType); + + //Filtering - Joint-Histogram Framework + for(int i=0; i<(int)Is.size(); i++) + { + Is[i] = filterCore(Is[i], F, wMap, r, nF,nI,mask); + } + float2D_release(wMap); + + //Postprocess F + //Convert input image back to the original type. + for(int i = 0; i < (int)Is.size(); i++) + { + if(I.depth()==CV_32F) + { + from32STo32F(Is[i],Is[i],iMap[i]); + delete []iMap[i]; + } + else if(I.depth()==CV_8U) + { + Is[i].convertTo(Is[i],CV_8U); + } + } + + //merge the channels + merge(Is, D); +} +} +} diff --git a/modules/ximgproc/test/test_weighted_median_filter.cpp b/modules/ximgproc/test/test_weighted_median_filter.cpp new file mode 100644 index 000000000..9571e84d5 --- /dev/null +++ b/modules/ximgproc/test/test_weighted_median_filter.cpp @@ -0,0 +1,107 @@ +/* + * 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 string getDataDir() +{ + return cvtest::TS::ptr()->get_data_path(); +} + +typedef tuple WMFParams; +typedef TestWithParam WeightedMedianFilterTest; + +TEST_P(WeightedMedianFilterTest, SplatSurfaceAccuracy) +{ + + WMFParams params = GetParam(); + Size size = get<0>(params); + WMFWeightType weightType = get<1>(params); + + RNG rnd(0); + + int guideCn = rnd.uniform(1, 2); + if(guideCn==2) guideCn++; //1 or 3 channels + Mat guide(size, CV_MAKE_TYPE(CV_8U, guideCn)); + randu(guide, 0, 255); + + Scalar surfaceValue; + int srcCn = rnd.uniform(1, 4); + rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255); + Mat src(size, CV_MAKE_TYPE(CV_8U, srcCn), surfaceValue); + + int r = int(rnd.uniform(3, 11)); + double sigma = rnd.uniform(9.0, 100.0); + + Mat res; + weightedMedianFilter(guide, src, res, r, sigma, weightType); + + double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels(); + EXPECT_LE(normL1, 1.0/64); +} + +TEST(WeightedMedianFilterTest, ReferenceAccuracy) +{ + string dir = getDataDir() + "cv/edgefilter"; + + Mat src = imread(dir + "/kodim23.png"); + Mat ref = imread(dir + "/fgs/kodim23_lambda=1000_sigma=10.png"); + + ASSERT_FALSE(src.empty()); + ASSERT_FALSE(ref.empty()); + + cv::setNumThreads(cv::getNumberOfCPUs()); + Mat res; + weightedMedianFilter(src, src, res, 7); + + double totalMaxError = 1.0/32.0*src.total()*src.channels(); + + EXPECT_LE(cvtest::norm(res, ref, NORM_L2), totalMaxError); +} + +INSTANTIATE_TEST_CASE_P(TypicalSET, WeightedMedianFilterTest, Combine(Values(szODD, szQVGA), Values(WMF_EXP, WMF_IV2, WMF_OFF))); + +}