diff --git a/modules/ximgproc/doc/ximgproc.bib b/modules/ximgproc/doc/ximgproc.bib index 3e1b32361..322b37121 100644 --- a/modules/ximgproc/doc/ximgproc.bib +++ b/modules/ximgproc/doc/ximgproc.bib @@ -55,3 +55,25 @@ year={2013}, organization={IEEE} } + +@article{Min2014, + title={Fast global image smoothing based on weighted least squares}, + author={Min, Dongbo and Choi, Sunghwan and Lu, Jiangbo and Ham, Bumsub and Sohn, Kwanghoon and Do, Minh N}, + journal={Image Processing, IEEE Transactions on}, + volume={23}, + number={12}, + pages={5638--5653}, + year={2014}, + publisher={IEEE} +} + +@inproceedings{Farbman2008, + title={Edge-preserving decompositions for multi-scale tone and detail manipulation}, + author={Farbman, Zeev and Fattal, Raanan and Lischinski, Dani and Szeliski, Richard}, + booktitle={ACM Transactions on Graphics (TOG)}, + volume={27}, + number={3}, + pages={67}, + year={2008}, + organization={ACM} +} diff --git a/modules/ximgproc/include/opencv2/ximgproc.hpp b/modules/ximgproc/include/opencv2/ximgproc.hpp index 69c0e7fe7..ac7328f7d 100644 --- a/modules/ximgproc/include/opencv2/ximgproc.hpp +++ b/modules/ximgproc/include/opencv2/ximgproc.hpp @@ -38,6 +38,7 @@ #define __OPENCV_XIMGPROC_HPP__ #include "ximgproc/edge_filter.hpp" +#include "ximgproc/disparity_filter.hpp" #include "ximgproc/structured_edge_detection.hpp" #include "ximgproc/seeds.hpp" diff --git a/modules/ximgproc/include/opencv2/ximgproc/disparity_filter.hpp b/modules/ximgproc/include/opencv2/ximgproc/disparity_filter.hpp index c6798b866..274368861 100644 --- a/modules/ximgproc/include/opencv2/ximgproc/disparity_filter.hpp +++ b/modules/ximgproc/include/opencv2/ximgproc/disparity_filter.hpp @@ -43,49 +43,147 @@ namespace cv { namespace ximgproc { +//! @addtogroup ximgproc_filters +//! @{ + +/** @brief Main interface for all disparity map filters. + */ class CV_EXPORTS_W DisparityFilter : public Algorithm { public: - CV_WRAP virtual void filter(InputArray disparity_map, InputArray left_view, OutputArray filtered_disparity_map,Rect ROI) = 0; -}; -///////////////////////////////////////////////////////////////////////////////////////////////// + /** @brief Apply filtering to the disparity map. -class CV_EXPORTS_W DisparityDTFilter : public DisparityFilter -{ -public: - CV_WRAP virtual double getSigmaSpatial() = 0; - CV_WRAP virtual void setSigmaSpatial(double _sigmaSpatial) = 0; - CV_WRAP virtual double getSigmaColor() = 0; - CV_WRAP virtual void setSigmaColor(double _sigmaColor) = 0; -}; + @param disparity_map_left disparity map of the left view, 1 channel, CV_16S type. Implicitly assumes that disparity + values are scaled by 16 (one-pixel disparity corresponds to the value of 16 in the disparity map). Disparity map + can have any resolution, it will be automatically resized to fit left_view resolution. -CV_EXPORTS_W -Ptr createDisparityDTFilter(); + @param left_view left view of the original stereo-pair to guide the filtering process, 8-bit single-channel + or three-channel image. -class CV_EXPORTS_W DisparityGuidedFilter : public DisparityFilter -{ -public: - CV_WRAP virtual double getEps() = 0; - CV_WRAP virtual void setEps(double _eps) = 0; - CV_WRAP virtual int getRadius() = 0; - CV_WRAP virtual void setRadius(int _radius) = 0; -}; + @param filtered_disparity_map output disparity map. -CV_EXPORTS_W -Ptr createDisparityGuidedFilter(); + @param ROI region of the disparity map to filter. + @param disparity_map_right optional argument, some implementations might also use the disparity map + of the right view to compute confidence maps, for instance. + + @param right_view optional argument, some implementations might also use the right view of the original + stereo-pair. + */ + CV_WRAP virtual void filter(InputArray disparity_map_left, InputArray left_view, OutputArray filtered_disparity_map, Rect ROI, InputArray disparity_map_right = Mat(), InputArray right_view = Mat()) = 0; +}; + +/** @brief Disparity map filter based on Weighted Least Squares filter (in form of Fast Global Smoother that +is a lot faster than traditional Weighted Least Squares filter implementations) and optional use of +left-right-consistency-based confidence to refine the results in half-occlusions and uniform areas. + */ class CV_EXPORTS_W DisparityWLSFilter : public DisparityFilter { +public: + /** filter parameters */ + + /** @brief Lambda is a parameter defining the amount of regularization during filtering. Larger values force + filtered disparity map edges to adhere more to source image edges. Typical value is 8000. + */ CV_WRAP virtual double getLambda() = 0; + /** @see getLambda */ CV_WRAP virtual void setLambda(double _lambda) = 0; + /** @brief SigmaColor is a parameter defining how sensitive the filtering process is to source image edges. + Large values can lead to disparity leakage through low-contrast edges. Small values can make the filter too + sensitive to noise and textures in the source image. Typical values range from 0.8 to 2.0. + */ CV_WRAP virtual double getSigmaColor() = 0; + /** @see getSigmaColor */ CV_WRAP virtual void setSigmaColor(double _sigma_color) = 0; + + /** confidence-related parameters */ + + /** @brief LRCthresh is a threshold of disparity difference used in left-right-consistency check during + confidence map computation. The default value of 24 (1.5 pixels) is virtually always good enough. + */ + CV_WRAP virtual int getLRCthresh() = 0; + /** @see getLRCthresh */ + CV_WRAP virtual void setLRCthresh(int _LRC_thresh) = 0; + /** @brief DepthDiscontinuityRadius is a parameter used in confidence computation. It defines the size of + low-confidence regions around depth discontinuities. For typical window sizes used in stereo matching the + optimal value is around 5. + */ + CV_WRAP virtual int getDepthDiscontinuityRadius() = 0; + /** @see getDepthDiscontinuityRadius */ + CV_WRAP virtual void setDepthDiscontinuityRadius(int _disc_radius) = 0; + /** @brief Get the confidence map that was used in the last filter call. It is a CV_32F one-channel image + with values ranging from 0.0 (totally untrusted regions of the raw disparity map) to 255.0 (regions containing + correct disparity values with a high degree of confidence). + */ + CV_WRAP virtual Mat getConfidenceMap() = 0; + }; +/** @brief Factory method, create instance of DisparityWLSFilter and execute the initialization routines. + +@param use_confidence filtering with confidence requires two disparity maps (for the left and right views) and is +approximately two times slower. However, quality is typically significantly better. +*/ CV_EXPORTS_W -Ptr createDisparityWLSFilter(); +Ptr createDisparityWLSFilter(bool use_confidence); + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +/** @brief Function for reading ground truth disparity maps. Supports basic Middlebury +and MPI-Sintel formats. Note that the resulting disparity map is scaled by 16. + +@param src_path path to the image, containing ground-truth disparity map + +@param dst output disparity map, CV_16S depth + +@result returns zero if successfully read the ground truth + */ +CV_EXPORTS +int readGT(String src_path,OutputArray dst); + +/** @brief Function for computing mean square error for disparity maps + +@param GT ground truth disparity map + +@param src disparity map to evaluate + +@param ROI region of interest + +@result returns mean square error between GT and src + */ +CV_EXPORTS +double computeMSE(InputArray GT, InputArray src, Rect ROI); + +/** @brief Function for computing the percent of "bad" pixels in the disparity map +(pixels where error is higher than a specified threshold) + +@param GT ground truth disparity map + +@param src disparity map to evaluate + +@param ROI region of interest + +@param thresh threshold used to determine "bad" pixels + +@result returns mean square error between GT and src + */ +CV_EXPORTS +double computeBadPixelPercent(InputArray GT, InputArray src, Rect ROI, int thresh=24/*1.5 pixels*/); + +/** @brief Function for creating a disparity map visualization (clamped CV_8U image) + +@param src input disparity map (CV_16S depth) + +@param dst output visualization + +@param scale disparity map will be multiplied by this value for visualization + */ +CV_EXPORTS +void getDisparityVis(InputArray src,OutputArray dst,double scale=1.0); +//! @} } } #endif diff --git a/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp b/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp index 129c00ba2..60be77765 100644 --- a/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp +++ b/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp @@ -316,19 +316,69 @@ proportional to sigmaSpace . CV_EXPORTS_W void jointBilateralFilter(InputArray joint, InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFAULT); -//! @} +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + -class CV_EXPORTS_W WeightedLeastSquaresFilter : public Algorithm +/** @brief Interface for implementations of Fast Global Smoother filter. + +For more details about this filter see @cite Min2014 and @cite Farbman2008 . +*/ +class CV_EXPORTS_W FastGlobalSmootherFilter : public Algorithm { public: + /** @brief Apply smoothing operation to the source image. + + @param src source image for filtering with unsigned 8-bit or signed 16-bit or floating-point 32-bit depth and up to 4 channels. + + @param dst destination image. + */ CV_WRAP virtual void filter(InputArray src, OutputArray dst) = 0; - virtual ~WeightedLeastSquaresFilter(); }; -CV_EXPORTS_W Ptr createWeightedLeastSquaresFilter(InputArray guide, double lambda, double sigma_color, int num_iter=3); +/** @brief Factory method, create instance of FastGlobalSmootherFilter and execute the initialization routines. + +@param guide image serving as guide for filtering. It should have 8-bit depth and either 1 or 3 channels. + +@param lambda parameter defining the amount of regularization + +@param sigma_color parameter, that is similar to color space sigma in bilateralFilter. + +@param lambda_attenuation internal parameter, defining how much lambda decreases after each iteration. Normally, +it should be 0.25. Setting it to 1.0 may lead to streaking artifacts. -CV_EXPORTS_W void weightedLeastSquaresFilter(InputArray guide, InputArray src, OutputArray dst, double lambda, double sigma_color, int num_iter=3); +@param num_iter number of iterations used for filtering, 3 is usually enough. +For more details about Fast Global Smoother parameters, see the original paper @cite Min2014. However, please note that +there are several differences. Lambda attenuation described in the paper is implemented a bit differently so do not +expect the results to be identical to those from the paper; sigma_color values from the paper should be multiplied by 255.0 to +achieve the same effect. Also, in case of image filtering where source and guide image are the same, authors +propose to dynamically update the guide image after each iteration. To maximize the performance this feature +was not implemented here. +*/ +CV_EXPORTS_W Ptr createFastGlobalSmootherFilter(InputArray guide, double lambda, double sigma_color, double lambda_attenuation=0.25, int num_iter=3); + +/** @brief Simple one-line Fast Global Smoother filter call. If you have multiple images to filter with the same +guide then use FastGlobalSmootherFilter interface to avoid extra computations. + +@param guide image serving as guide for filtering. It should have 8-bit depth and either 1 or 3 channels. + +@param src source image for filtering with unsigned 8-bit or signed 16-bit or floating-point 32-bit depth and up to 4 channels. + +@param dst destination image. + +@param lambda parameter defining the amount of regularization + +@param sigma_color parameter, that is similar to color space sigma in bilateralFilter. + +@param lambda_attenuation internal parameter, defining how much lambda decreases after each iteration. Normally, +it should be 0.25. Setting it to 1.0 may lead to streaking artifacts. + +@param num_iter number of iterations used for filtering, 3 is usually enough. +*/ +CV_EXPORTS_W void fastGlobalSmootherFilter(InputArray guide, InputArray src, OutputArray dst, double lambda, double sigma_color, double lambda_attenuation=0.25, int num_iter=3); + +//! @} } } #endif diff --git a/modules/ximgproc/perf/perf_disparity_wls_filter.cpp b/modules/ximgproc/perf/perf_disparity_wls_filter.cpp new file mode 100644 index 000000000..c4fbc1298 --- /dev/null +++ b/modules/ximgproc/perf/perf_disparity_wls_filter.cpp @@ -0,0 +1,169 @@ +/* + * 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" +#include "opencv2/ximgproc/disparity_filter.hpp" + +namespace cvtest +{ + +using std::tr1::tuple; +using std::tr1::get; +using namespace perf; +using namespace testing; +using namespace cv; +using namespace cv::ximgproc; + +void MakeArtificialExample(RNG rng, Mat& dst_left_view, Mat& dst_left_disparity_map, Mat& dst_right_disparity_map, Rect& dst_ROI); + +CV_ENUM(GuideTypes, CV_8UC3); +CV_ENUM(SrcTypes, CV_16S); +typedef tuple DisparityWLSParams; + +typedef TestBaseWithParam DisparityWLSFilterPerfTest; + +PERF_TEST_P( DisparityWLSFilterPerfTest, perf, Combine(GuideTypes::all(), SrcTypes::all(), Values(sz720p), Values(true,false), Values(true,false)) ) +{ + RNG rng(0); + + DisparityWLSParams params = GetParam(); + int guideType = get<0>(params); + int srcType = get<1>(params); + Size sz = get<2>(params); + bool use_conf = get<3>(params); + bool use_downscale = get<4>(params); + + Mat guide(sz, guideType); + Mat disp_left(sz, srcType); + Mat disp_right(sz, srcType); + Mat dst(sz, srcType); + Rect ROI; + + MakeArtificialExample(rng,guide,disp_left,disp_right,ROI); + if(use_downscale) + { + resize(disp_left,disp_left,Size(),0.5,0.5); + disp_left/=2; + resize(disp_right,disp_right,Size(),0.5,0.5); + disp_right/=2; + ROI = Rect(ROI.x/2,ROI.y/2,ROI.width/2,ROI.height/2); + } + + cv::setNumThreads(cv::getNumberOfCPUs()); + TEST_CYCLE_N(10) + { + Ptr wls_filter = createDisparityWLSFilter(use_conf); + wls_filter->filter(disp_left,guide,dst,ROI,disp_right); + } + + SANITY_CHECK(dst); +} + +void MakeArtificialExample(RNG rng, Mat& dst_left_view, Mat& dst_left_disparity_map, Mat& dst_right_disparity_map, Rect& dst_ROI) +{ + int w = dst_left_view.cols; + int h = dst_left_view.rows; + + //params: + unsigned char bg_level = (unsigned char)rng.uniform(0.0,255.0); + unsigned char fg_level = (unsigned char)rng.uniform(0.0,255.0); + int rect_width = (int)rng.uniform(w/16,w/2); + int rect_height = (int)rng.uniform(h/16,h/2); + int rect_disparity = (int)(0.15*w); //typical maximum disparity value + double sigma = 6.0; + + int rect_x_offset = (w-rect_width) /2; + int rect_y_offset = (h-rect_height)/2; + + if(dst_left_view.channels()==3) + dst_left_view = Scalar(Vec3b(bg_level,bg_level,bg_level)); + else + dst_left_view = Scalar(bg_level); + dst_left_disparity_map = Scalar(0); + dst_right_disparity_map = Scalar(0); + Mat dst_left_view_rect = Mat(dst_left_view, Rect(rect_x_offset,rect_y_offset,rect_width,rect_height)); + Mat dst_left_disparity_map_rect = Mat(dst_left_disparity_map,Rect(rect_x_offset,rect_y_offset,rect_width,rect_height)); + if(dst_left_view.channels()==3) + dst_left_view_rect = Scalar(Vec3b(fg_level,fg_level,fg_level)); + else + dst_left_view_rect = Scalar(fg_level); + dst_left_disparity_map_rect = Scalar(16*rect_disparity); + + rect_x_offset-=rect_disparity; + Mat dst_right_disparity_map_rect = Mat(dst_right_disparity_map,Rect(rect_x_offset,rect_y_offset,rect_width,rect_height)); + dst_right_disparity_map_rect = Scalar(-16*rect_disparity); + + //add some gaussian noise: + unsigned char *l; + short *ldisp, *rdisp; + for(int i=0;i(l[0] + rng.gaussian(sigma)); + l[1] = saturate_cast(l[1] + rng.gaussian(sigma)); + l[2] = saturate_cast(l[2] + rng.gaussian(sigma)); + l+=3; + ldisp[0] = saturate_cast(ldisp[0] + rng.gaussian(sigma)); + ldisp++; + rdisp[0] = saturate_cast(rdisp[0] + rng.gaussian(sigma)); + rdisp++; + } + } + else + { + for(int j=0;j(l[0] + rng.gaussian(sigma)); + l++; + ldisp[0] = saturate_cast(ldisp[0] + rng.gaussian(sigma)); + ldisp++; + rdisp[0] = saturate_cast(rdisp[0] + rng.gaussian(sigma)); + rdisp++; + } + } + } + + dst_ROI = Rect(rect_disparity,0,w-rect_disparity,h); +} + +} diff --git a/modules/ximgproc/perf/perf_fgs_filter.cpp b/modules/ximgproc/perf/perf_fgs_filter.cpp new file mode 100644 index 000000000..3c97e0905 --- /dev/null +++ b/modules/ximgproc/perf/perf_fgs_filter.cpp @@ -0,0 +1,81 @@ +/* + * 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; + +CV_ENUM(GuideTypes, CV_8UC1, CV_8UC3); +CV_ENUM(SrcTypes, CV_8UC1, CV_8UC3, CV_16SC1, CV_16SC3, CV_32FC1, CV_32FC3); +typedef tuple FGSParams; + +typedef TestBaseWithParam FGSFilterPerfTest; + +PERF_TEST_P( FGSFilterPerfTest, perf, Combine(GuideTypes::all(), SrcTypes::all(), Values(sz720p)) ) +{ + RNG rng(0); + + FGSParams params = GetParam(); + int guideType = get<0>(params); + int srcType = get<1>(params); + Size sz = get<2>(params); + + Mat guide(sz, guideType); + Mat src(sz, srcType); + Mat dst(sz, srcType); + + declare.in(guide, src, WARMUP_RNG).out(dst).tbb_threads(cv::getNumberOfCPUs()); + + cv::setNumThreads(cv::getNumberOfCPUs()); + TEST_CYCLE_N(10) + { + double lambda = rng.uniform(500.0, 10000.0); + double sigma = rng.uniform(1.0, 100.0); + fastGlobalSmootherFilter(guide,src,dst,lambda,sigma); + } + + SANITY_CHECK(dst); +} + +} \ No newline at end of file diff --git a/modules/ximgproc/samples/disparity_filtering.cpp b/modules/ximgproc/samples/disparity_filtering.cpp index e55c8eb5b..5ed66ec69 100644 --- a/modules/ximgproc/samples/disparity_filtering.cpp +++ b/modules/ximgproc/samples/disparity_filtering.cpp @@ -4,215 +4,306 @@ #include "opencv2/highgui.hpp" #include "opencv2/core/utility.hpp" #include "opencv2/ximgproc/disparity_filter.hpp" -#include +#include #include -#include -#include - -#if defined(_WIN32) -#include -#else -#include -#endif using namespace cv; using namespace cv::ximgproc; using namespace std; -#define UNKNOWN_DISPARITY 16320 - -static void print_help() -{ - printf("\nDemo for disparity filtering, evaluating speed and performance of different filters\n"); - printf("\nUsage: disparity_filtering.exe \n"); -} +Rect computeROI(Size2i src_sz, Ptr matcher_instance); + +const String keys = + "{help h usage ? | | print this message }" + "{@left |../data/aloeL.jpg | left view of the stereopair }" + "{@right |../data/aloeR.jpg | right view of the stereopair }" + "{GT |../data/aloeGT.png| optional ground-truth disparity (MPI-Sintel or Middlebury format) }" + "{dst_path |None | optional path to save the resulting filtered disparity map }" + "{dst_raw_path |None | optional path to save raw disparity map before filtering }" + "{algorithm |bm | stereo matching method (bm or sgbm) }" + "{filter |wls_conf | used post-filtering (wls_conf or wls_no_conf) }" + "{no-display | | don't display results }" + "{no-downscale | | prevent stereo matching on downscaled views }" + "{dst_conf_path |None | optional path to save the confidence map used in filtering }" + "{vis_mult |1.0 | coefficient used to scale disparity map visualizations }" + "{max_disparity |160 | parameter of stereo matching }" + "{window_size |19 | parameter of stereo matching }" + "{wls_lambda |8000.0 | parameter of post-filtering }" + "{wls_sigma |1.0 | parameter of post-filtering }" + ; -struct dataset_entry +int main(int argc, char** argv) { - string name; - string dataset_folder; - string left_file,right_file,GT_file; - dataset_entry(string _dataset_folder): dataset_folder(_dataset_folder){} - void readEntry(Mat& dst_left,Mat& dst_right,Mat& dst_GT) + CommandLineParser parser(argc,argv,keys); + parser.about("Disparity Filtering Demo"); + if (parser.has("help")) { - dst_left = imread(dataset_folder+"/"+left_file, IMREAD_COLOR); - dst_right = imread(dataset_folder+"/"+right_file, IMREAD_COLOR); - Mat raw_disp = imread(dataset_folder+"/"+GT_file, IMREAD_COLOR); - dst_GT = Mat(raw_disp.rows,raw_disp.cols,CV_16S); - for(int i=0;i(i, j); - dst_GT.at(i,j) = 64*bgrPixel.val[2]+bgrPixel.val[1]/4; //16-multiplied disparity - } + parser.printMessage(); + return 0; } -}; -struct config -{ - Ptr matcher_instance; - Ptr filter_instance; - config(Ptr _matcher_instance,Ptr _filter_instance) + String left_im = parser.get(0); + String right_im = parser.get(1); + String GT_path = parser.get("GT"); + + String dst_path = parser.get("dst_path"); + String dst_raw_path = parser.get("dst_raw_path"); + String dst_conf_path = parser.get("dst_conf_path"); + String algo = parser.get("algorithm"); + String filter = parser.get("filter"); + bool no_display = parser.has("no-display"); + bool no_downscale = parser.has("no-downscale"); + int max_disp = parser.get("max_disparity"); + int wsize = parser.get("window_size"); + double lambda = parser.get("wls_lambda"); + double sigma = parser.get("wls_sigma"); + double vis_mult = parser.get("vis_mult"); + + if (!parser.check()) { - matcher_instance = _matcher_instance; - filter_instance = _filter_instance; + parser.printErrors(); + return -1; } - config() {} -}; -void operator>>(const FileNode& node,dataset_entry& entry); -double computeMSE(Mat& GT, Mat& src, Rect ROI); -double computeBadPixelPercent(Mat& GT, Mat& src, Rect ROI, int thresh=24/*1.5 pixels*/); -void getDisparityVis(Mat& disparity_map,Mat& dst); -Rect computeROI(Size2i src_sz, Ptr matcher_instance); -void setConfigsForTesting(map& cfgs); -void CreateDir(string path); - -int main(int argc, char** argv) -{ - if(argc < 3) + Mat left = imread(left_im ,IMREAD_COLOR); + if ( left.empty() ) { - print_help(); - return 0; + cout<<"Cannot read image file: "< configs_for_testing; - setConfigsForTesting(configs_for_testing); - CreateDir(res_folder); + Mat right = imread(right_im,IMREAD_COLOR); + if ( right.empty() ) + { + cout<<"Cannot read image file: "<::iterator cfg = configs_for_testing.begin(); cfg != configs_for_testing.end(); cfg++) + bool noGT; + Mat GT_disp; + if (GT_path=="../data/aloeGT.png" && left_im!="../data/aloeL.jpg") + noGT=true; + else { - string vis_folder = res_folder+"/vis_"+cfg->first; - CreateDir(vis_folder); - - string cfg_file_name = res_folder+"/"+cfg->first+"_res.csv"; - FILE* cur_cfg_res_file = fopen(cfg_file_name.c_str(),"w"); - fprintf(cur_cfg_res_file,"Name,MSE,MSE after postfiltering,Percent bad,Percent bad after postfiltering,Matcher Execution Time(s),Filter Execution Time(s)\n"); - - printf("Processing configuration: %s\n",cfg->first.c_str()); - - FileStorage fs(dataset_folder + "/_dataset.xml", FileStorage::READ); - FileNode n = fs["data_set"]; - double MSE_pre,percent_pre,MSE_post,percent_post,matching_time,filtering_time; - double average_MSE_pre=0,average_percent_pre=0,average_MSE_post=0, - average_percent_post=0,average_matching_time=0,average_filtering_time=0; - int cnt = 0; - for (FileNodeIterator it = n.begin(); it != n.end(); it++) + noGT=false; + if(readGT(GT_path,GT_disp)!=0) { - dataset_entry entry(dataset_folder); - (*it)>>entry; - printf("%s ",entry.name.c_str()); - Mat left,right,GT; - entry.readEntry(left,right,GT); - Mat raw_disp; - Mat left_gray; cvtColor(left, left_gray, COLOR_BGR2GRAY ); - Mat right_gray; cvtColor(right, right_gray, COLOR_BGR2GRAY ); - matching_time = (double)getTickCount(); - cfg->second.matcher_instance->compute(left_gray,right_gray,raw_disp); - matching_time = ((double)getTickCount() - matching_time)/getTickFrequency(); - - Rect ROI = computeROI(left.size(),cfg->second.matcher_instance); - Mat filtered_disp; - filtering_time = (double)getTickCount(); - cfg->second.filter_instance->filter(raw_disp,left,filtered_disp,ROI); - filtering_time = ((double)getTickCount() - filtering_time)/getTickFrequency(); + cout<<"Cannot read ground truth image file: "< left_matcher = StereoBM::create(max_disp,wsize); + left_matcher->setMinDisparity(0); + Ptr right_matcher = StereoBM::create(max_disp,wsize); + right_matcher->setMinDisparity(-max_disp+1); + left_matcher->setTextureThreshold(0); + left_matcher->setUniquenessRatio(0); + right_matcher->setTextureThreshold(0); + right_matcher->setUniquenessRatio(0); + cvtColor(left_for_matcher, left_for_matcher, COLOR_BGR2GRAY); + cvtColor(right_for_matcher, right_for_matcher, COLOR_BGR2GRAY); + ROI = computeROI(left_for_matcher.size(),left_matcher); - average_matching_time+=matching_time; average_filtering_time+=filtering_time; - average_MSE_pre+=MSE_pre; average_percent_pre+=percent_pre; - average_MSE_post+=MSE_post; average_percent_post+=percent_post; - cnt++; + matching_time = (double)getTickCount(); + left_matcher-> compute(left_for_matcher, right_for_matcher,left_disp); + right_matcher->compute(right_for_matcher,left_for_matcher, right_disp); + matching_time = ((double)getTickCount() - matching_time)/getTickFrequency(); + } + else if(algo=="sgbm") + { + Ptr left_matcher = StereoSGBM::create(0,max_disp,wsize); + left_matcher->setMinDisparity(0); + Ptr right_matcher = StereoSGBM::create(-max_disp+1,max_disp,wsize); + left_matcher->setUniquenessRatio(0); + left_matcher->setDisp12MaxDiff(1000000); + left_matcher->setSpeckleWindowSize(0); + right_matcher->setUniquenessRatio(0); + right_matcher->setDisp12MaxDiff(1000000); + right_matcher->setSpeckleWindowSize(0); + ROI = computeROI(left_for_matcher.size(),left_matcher); - // dump visualizations: - imwrite(vis_folder + "/" + entry.name + "_left.png",left); - Mat GT_vis,raw_disp_vis,filtered_disp_vis; - getDisparityVis(GT,GT_vis); - getDisparityVis(raw_disp,raw_disp_vis); - getDisparityVis(filtered_disp,filtered_disp_vis); - imwrite(vis_folder + "/" + entry.name + "_disparity_GT.png",GT_vis); - imwrite(vis_folder + "/" + entry.name + "_disparity_raw.png",raw_disp_vis); - imwrite(vis_folder + "/" + entry.name + "_disparity_filtered.png",filtered_disp_vis); + matching_time = (double)getTickCount(); + left_matcher-> compute(left_for_matcher, right_for_matcher,left_disp); + right_matcher->compute(right_for_matcher,left_for_matcher, right_disp); + matching_time = ((double)getTickCount() - matching_time)/getTickFrequency(); + } + else + { + cout<<"Unsupported algorithm"; + return -1; + } - printf("- Done\n"); + Ptr wls_filter = createDisparityWLSFilter(true); + wls_filter->setLambda(lambda); + wls_filter->setSigmaColor(sigma); + filtering_time = (double)getTickCount(); + wls_filter->filter(left_disp,left,filtered_disp,ROI,right_disp); + filtering_time = ((double)getTickCount() - filtering_time)/getTickFrequency(); + conf_map = wls_filter->getConfidenceMap(); + if(!no_downscale) + { + // upscale raw disparity and ROI back for a proper comparison: + resize(left_disp,left_disp,Size(),2.0,2.0); + left_disp = left_disp*2.0; + ROI = Rect(ROI.x*2,ROI.y*2,ROI.width*2,ROI.height*2); } - fprintf(cur_cfg_res_file,"%s,%.1f,%.1f,%.1f,%.1f,%.3f,%.3f\n","average",average_MSE_pre/cnt, - average_MSE_post/cnt,average_percent_pre/cnt,average_percent_post/cnt, - average_matching_time/cnt,average_filtering_time/cnt); - fclose(cur_cfg_res_file); } - return 0; -} - -void operator>>(const FileNode& node,dataset_entry& entry) -{ - node["name"] >> entry.name; - node["left_file"] >> entry.left_file; - node["right_file"] >> entry.right_file; - node["GT_file"] >> entry.GT_file; -} + else if(filter=="wls_no_conf") + { + left_for_matcher = left.clone(); + right_for_matcher = right.clone(); -double computeMSE(Mat& GT, Mat& src, Rect ROI) -{ - double res = 0; - Mat GT_ROI(GT,ROI); - Mat src_ROI(src,ROI); - int cnt=0; - for(int i=0;i(i,j)!=UNKNOWN_DISPARITY) - { - res += (GT_ROI.at(i,j) - src_ROI.at(i,j))*(GT_ROI.at(i,j) - src_ROI.at(i,j)); - cnt++; - } + Ptr matcher = StereoBM::create(max_disp,wsize); + matcher->setTextureThreshold(0); + matcher->setUniquenessRatio(0); + cvtColor(left_for_matcher, left_for_matcher, COLOR_BGR2GRAY); + cvtColor(right_for_matcher, right_for_matcher, COLOR_BGR2GRAY); + ROI = computeROI(left_for_matcher.size(),matcher); + + matching_time = (double)getTickCount(); + matcher->compute(left_for_matcher,right_for_matcher,left_disp); + matching_time = ((double)getTickCount() - matching_time)/getTickFrequency(); } - res /= cnt*256; - return res; -} + else if(algo=="sgbm") + { + Ptr matcher = StereoSGBM::create(0,max_disp,wsize); + matcher->setUniquenessRatio(0); + matcher->setDisp12MaxDiff(1000000); + matcher->setSpeckleWindowSize(0); + ROI = computeROI(left_for_matcher.size(),matcher); -double computeBadPixelPercent(Mat& GT, Mat& src, Rect ROI, int thresh) -{ - int bad_pixel_num = 0; - Mat GT_ROI(GT,ROI); - Mat src_ROI(src,ROI); - int cnt=0; - for(int i=0;icompute(left_for_matcher,right_for_matcher,left_disp); + matching_time = ((double)getTickCount() - matching_time)/getTickFrequency(); + } + else { - if(GT_ROI.at(i,j)!=UNKNOWN_DISPARITY) - { - if( abs(GT_ROI.at(i,j) - src_ROI.at(i,j))>=thresh ) - bad_pixel_num++; - cnt++; - } + cout<<"Unsupported algorithm"; + return -1; } - return (100.0*bad_pixel_num)/cnt; -} -void getDisparityVis(Mat& disparity_map,Mat& dst) -{ - dst = Mat(disparity_map.rows,disparity_map.cols,CV_8UC3); - for(int i=0;i wls_filter = createDisparityWLSFilter(false); + wls_filter->setLambda(lambda); + wls_filter->setSigmaColor(sigma); + filtering_time = (double)getTickCount(); + wls_filter->filter(left_disp,left,filtered_disp,ROI); + filtering_time = ((double)getTickCount() - filtering_time)/getTickFrequency(); + } + else + { + cout<<"Unsupported filter"; + return -1; + } + + //collect and print all the stats: + cout.precision(2); + cout<<"Matching time: "<(i,j)==UNKNOWN_DISPARITY) - dst.at(i,j) = Vec3b(0,0,0); - else - dst.at(i,j) = Vec3b(saturate_cast(disparity_map.at(i,j)/8), - saturate_cast(disparity_map.at(i,j)/8), - saturate_cast(disparity_map.at(i,j)/8)); + Mat GT_disp_vis; + getDisparityVis(GT_disp,GT_disp_vis,vis_mult); + namedWindow("ground-truth disparity", WINDOW_AUTOSIZE); + imshow("ground-truth disparity", GT_disp_vis); } + + Mat raw_disp_vis; + getDisparityVis(left_disp,raw_disp_vis,vis_mult); + namedWindow("raw disparity", WINDOW_AUTOSIZE); + imshow("raw disparity", raw_disp_vis); + Mat filtered_disp_vis; + getDisparityVis(filtered_disp,filtered_disp_vis,vis_mult); + namedWindow("filtered disparity", WINDOW_AUTOSIZE); + imshow("filtered disparity", filtered_disp_vis); + waitKey(); + } + + return 0; } Rect computeROI(Size2i src_sz, Ptr matcher_instance) @@ -225,34 +316,10 @@ Rect computeROI(Size2i src_sz, Ptr matcher_instance) int minD = min_disparity, maxD = min_disparity + num_disparities - 1; int xmin = maxD + bs2; - int xmax = src_sz.width - minD - bs2; + int xmax = src_sz.width + minD - bs2; int ymin = bs2; int ymax = src_sz.height - bs2; Rect r(xmin, ymin, xmax - xmin, ymax - ymin); return r; } - -void setConfigsForTesting(map& cfgs) -{ - Ptr stereobm_matcher = StereoBM::create(128,21); - stereobm_matcher->setTextureThreshold(0); - stereobm_matcher->setUniquenessRatio(0); - - Ptr wls_filter = createDisparityWLSFilter(); - Ptr dt_filter = createDisparityDTFilter(); - Ptr guided_filter = createDisparityGuidedFilter(); - - cfgs["stereobm_wls"] = config(stereobm_matcher,wls_filter); - cfgs["stereobm_dtf"] = config(stereobm_matcher,dt_filter); - cfgs["stereobm_gf"] = config(stereobm_matcher,guided_filter); -} - -void CreateDir(string path) -{ -#if defined(_WIN32) - _mkdir(path.c_str()); -#else - mkdir(path.c_str(), 0777); -#endif -} diff --git a/modules/ximgproc/src/disparity_filters.cpp b/modules/ximgproc/src/disparity_filters.cpp index c162cf024..ec4460ae8 100644 --- a/modules/ximgproc/src/disparity_filters.cpp +++ b/modules/ximgproc/src/disparity_filters.cpp @@ -36,149 +36,429 @@ #include "precomp.hpp" #include "opencv2/ximgproc/disparity_filter.hpp" +#include "opencv2/highgui.hpp" #include +#include namespace cv { namespace ximgproc { -void setROI(InputArray disparity_map, InputArray left_view, OutputArray filtered_disparity_map, - Mat& disp,Mat& src,Mat& dst,Rect ROI); +using std::vector; +#define EPS 1e-43f -class DisparityDTFilterImpl : public DisparityDTFilter +class DisparityWLSFilterImpl : public DisparityWLSFilter { protected: - double sigmaSpatial,sigmaColor; - void init(double _sigmaSpatial, double _sigmaColor) + double lambda,sigma_color; + bool use_confidence; + Mat confidence_map; + int LRC_thresh,depth_discontinuity_radius; + float depth_discontinuity_roll_off_factor; + float resize_factor; + int num_stripes; + + void init(double _lambda, double _sigma_color, bool _use_confidence); + void computeDepthDiscontinuityMaps(Mat& left_disp, Mat& right_disp, Mat& left_dst, Mat& right_dst, Rect ROI); + void computeConfidenceMap(InputArray left_disp, InputArray right_disp, Rect ROI); + +protected: + struct ComputeDiscontinuityAwareLRC_ParBody : public ParallelLoopBody { - sigmaColor = _sigmaColor; - sigmaSpatial = _sigmaSpatial; - } -public: - double getSigmaSpatial() {return sigmaSpatial;} - void setSigmaSpatial(double _sigmaSpatial) {sigmaSpatial = _sigmaSpatial;} - double getSigmaColor() {return sigmaColor;} - void setSigmaColor(double _sigmaColor) {sigmaColor = _sigmaColor;} + DisparityWLSFilterImpl* wls; + Mat *left_disp, *right_disp; + Mat *left_disc, *right_disc, *dst; + Rect left_ROI, right_ROI; + int nstripes, stripe_sz; + + ComputeDiscontinuityAwareLRC_ParBody(DisparityWLSFilterImpl& _wls, Mat& _left_disp, Mat& _right_disp, Mat& _left_disc, Mat& _right_disc, Mat& _dst, Rect _left_ROI, Rect _right_ROI, int _nstripes); + void operator () (const Range& range) const; + }; - static Ptr create() + struct ComputeDepthDisc_ParBody : public ParallelLoopBody { - DisparityDTFilterImpl *dtf = new DisparityDTFilterImpl(); - dtf->init(25.0,60.0); - return Ptr(dtf); + DisparityWLSFilterImpl* wls; + Mat *disp,*disp_squares,*dst; + int nstripes, stripe_sz; + + ComputeDepthDisc_ParBody(DisparityWLSFilterImpl& _wls, Mat& _disp, Mat& _disp_squares, Mat& _dst, int _nstripes); + void operator () (const Range& range) const; + }; + + typedef void (DisparityWLSFilterImpl::*MatOp)(Mat& src, Mat& dst); + + struct ParallelMatOp_ParBody : public ParallelLoopBody + { + DisparityWLSFilterImpl* wls; + vector ops; + vector src; + vector dst; + + ParallelMatOp_ParBody(DisparityWLSFilterImpl& _wls, vector _ops, vector& _src, vector& _dst); + void operator () (const Range& range) const; + }; + + void boxFilterOp(Mat& src,Mat& dst) + { + int rad = (int)ceil(resize_factor*depth_discontinuity_radius); + boxFilter(src,dst,CV_32F,Size(2*rad+1,2*rad+1),Point(-1,-1)); } - void filter(InputArray disparity_map, InputArray left_view, OutputArray filtered_disparity_map,Rect ROI) + void sqrBoxFilterOp(Mat& src,Mat& dst) { - Mat disp,src,dst; - setROI(disparity_map,left_view,filtered_disparity_map,disp,src,dst,ROI); + int rad = (int)ceil(resize_factor*depth_discontinuity_radius); + sqrBoxFilter(src,dst,CV_32F,Size(2*rad+1,2*rad+1),Point(-1,-1)); + } - Mat disp_32F,dst_32F; - disp.convertTo(disp_32F,CV_32F); - dtFilter(src,disp_32F,dst_32F,sigmaSpatial,sigmaColor); - dst_32F.convertTo(dst,CV_16S); + void copyToOp(Mat& src,Mat& dst) + { + src.copyTo(dst); } + +public: + static Ptr create(bool _use_confidence); + void filter(InputArray disparity_map_left, InputArray left_view, OutputArray filtered_disparity_map, Rect ROI, InputArray disparity_map_right, InputArray); + + double getLambda() {return lambda;} + void setLambda(double _lambda) {lambda = _lambda;} + double getSigmaColor() {return sigma_color;} + void setSigmaColor(double _sigma_color) {sigma_color = _sigma_color;} + Mat getConfidenceMap() {return confidence_map;} + int getLRCthresh() {return LRC_thresh;} + void setLRCthresh(int _LRC_thresh) {LRC_thresh = _LRC_thresh;} + int getDepthDiscontinuityRadius() {return depth_discontinuity_radius;} + void setDepthDiscontinuityRadius(int _disc_radius) {depth_discontinuity_radius = _disc_radius;} }; -CV_EXPORTS_W -Ptr createDisparityDTFilter() +void DisparityWLSFilterImpl::init(double _lambda, double _sigma_color, bool _use_confidence) { - return Ptr(DisparityDTFilterImpl::create()); + lambda = _lambda; + sigma_color = _sigma_color; + use_confidence = _use_confidence; + confidence_map = Mat(); + LRC_thresh = 24; + depth_discontinuity_radius = 5; + depth_discontinuity_roll_off_factor = 0.001f; + resize_factor = 1.0; + num_stripes = getNumThreads(); } -////////////////////////////////////////////////////////////////////////////////////////////////////// - -class DisparityGuidedFilterImpl : public DisparityGuidedFilter +void DisparityWLSFilterImpl::computeDepthDiscontinuityMaps(Mat& left_disp, Mat& right_disp, Mat& left_dst, Mat& right_dst, Rect ROI) { -protected: - int radius; - double eps; - void init(int _radius, double _eps) + Rect right_ROI(left_disp.cols-(ROI.x+ROI.width),ROI.y,ROI.width,ROI.height); + Mat left_disp_ROI (left_disp, ROI); + Mat right_disp_ROI(right_disp,right_ROI); + Mat ldisp,rdisp,ldisp_squared,rdisp_squared; + { - radius = _radius; - eps = _eps; + vector _src; _src.push_back(&left_disp_ROI);_src.push_back(&right_disp_ROI); + _src.push_back(&left_disp_ROI);_src.push_back(&right_disp_ROI); + vector _dst; _dst.push_back(&ldisp); _dst.push_back(&rdisp); + _dst.push_back(&ldisp_squared);_dst.push_back(&rdisp_squared); + vector _ops; _ops.push_back(&DisparityWLSFilterImpl::copyToOp); _ops.push_back(&DisparityWLSFilterImpl::copyToOp); + _ops.push_back(&DisparityWLSFilterImpl::copyToOp); _ops.push_back(&DisparityWLSFilterImpl::copyToOp); + parallel_for_(Range(0,4),ParallelMatOp_ParBody(*this,_ops,_src,_dst)); } -public: - double getEps() {return eps;} - void setEps(double _eps) {eps = _eps;} - int getRadius() {return radius;} - void setRadius(int _radius) {radius = _radius;} - static Ptr create() { - DisparityGuidedFilterImpl *gf = new DisparityGuidedFilterImpl(); - gf->init(20,100.0); - return Ptr(gf); + vector _src; _src.push_back(&ldisp); _src.push_back(&rdisp); + _src.push_back(&ldisp_squared);_src.push_back(&rdisp_squared); + vector _dst; _dst.push_back(&ldisp); _dst.push_back(&rdisp); + _dst.push_back(&ldisp_squared);_dst.push_back(&rdisp_squared); + vector _ops; _ops.push_back(&DisparityWLSFilterImpl::boxFilterOp); _ops.push_back(&DisparityWLSFilterImpl::boxFilterOp); + _ops.push_back(&DisparityWLSFilterImpl::sqrBoxFilterOp);_ops.push_back(&DisparityWLSFilterImpl::sqrBoxFilterOp); + parallel_for_(Range(0,4),ParallelMatOp_ParBody(*this,_ops,_src,_dst)); } - void filter(InputArray disparity_map, InputArray left_view, OutputArray filtered_disparity_map,Rect ROI) + left_dst = Mat::zeros(left_disp.rows,left_disp.cols,CV_32F); + right_dst = Mat::zeros(right_disp.rows,right_disp.cols,CV_32F); + Mat left_dst_ROI (left_dst,ROI); + Mat right_dst_ROI(right_dst,right_ROI); + + parallel_for_(Range(0,num_stripes),ComputeDepthDisc_ParBody(*this,ldisp,ldisp_squared,left_dst_ROI ,num_stripes)); + parallel_for_(Range(0,num_stripes),ComputeDepthDisc_ParBody(*this,rdisp,rdisp_squared,right_dst_ROI,num_stripes)); +} + + +void DisparityWLSFilterImpl::computeConfidenceMap(InputArray left_disp, InputArray right_disp, Rect ROI) +{ + Mat ldisp = left_disp.getMat(); + Mat rdisp = right_disp.getMat(); + Mat depth_discontinuity_map_left,depth_discontinuity_map_right; + computeDepthDiscontinuityMaps(ldisp,rdisp,depth_discontinuity_map_left,depth_discontinuity_map_right,ROI); + + Rect right_ROI(ldisp.cols-(ROI.x+ROI.width),ROI.y,ROI.width,ROI.height); + confidence_map = depth_discontinuity_map_left; + + parallel_for_(Range(0,num_stripes),ComputeDiscontinuityAwareLRC_ParBody(*this,ldisp,rdisp, depth_discontinuity_map_left,depth_discontinuity_map_right,confidence_map,ROI,right_ROI,num_stripes)); + confidence_map = 255.0f*confidence_map; +} + +Ptr DisparityWLSFilterImpl::create(bool _use_confidence) +{ + DisparityWLSFilterImpl *wls = new DisparityWLSFilterImpl(); + wls->init(8000.0,1.0,_use_confidence); + return Ptr(wls); +} + +void DisparityWLSFilterImpl::filter(InputArray disparity_map_left, InputArray left_view, OutputArray filtered_disparity_map, Rect ROI, InputArray disparity_map_right, InputArray) +{ + CV_Assert( !disparity_map_left.empty() && (disparity_map_left.depth() == CV_16S) && (disparity_map_left.channels() == 1) ); + CV_Assert( !left_view.empty() && (left_view.depth() == CV_8U) && (left_view.channels() == 3 || left_view.channels() == 1) ); + Mat disp,src,dst; + if(disparity_map_left.size()!=left_view.size()) + resize_factor = disparity_map_left.cols()/(float)left_view.cols(); + else + resize_factor = 1.0; + + if(!use_confidence) { - Mat disp,src,dst; - setROI(disparity_map,left_view,filtered_disparity_map,disp,src,dst,ROI); + Mat disp_full_size = disparity_map_left.getMat(); + Mat src_full_size = left_view.getMat(); + if(disp_full_size.size!=src_full_size.size) + { + float x_ratio = src_full_size.cols/(float)disp_full_size.cols; + float y_ratio = src_full_size.rows/(float)disp_full_size.rows; + resize(disp_full_size,disp_full_size,src_full_size.size()); + disp_full_size = disp_full_size*x_ratio; + ROI = Rect((int)(ROI.x*x_ratio),(int)(ROI.y*y_ratio),(int)(ROI.width*x_ratio),(int)(ROI.height*y_ratio)); + } + disp = Mat(disp_full_size,ROI); + src = Mat(src_full_size ,ROI); + filtered_disparity_map.create(disp_full_size.size(), disp_full_size.type()); + Mat& dst_full_size = filtered_disparity_map.getMatRef(); + dst_full_size = Scalar(-16); + dst = Mat(dst_full_size,ROI); + Mat filtered_disp; + fastGlobalSmootherFilter(src,disp,filtered_disp,lambda,sigma_color); + filtered_disp.copyTo(dst); + } + else + { + CV_Assert( !disparity_map_right.empty() && (disparity_map_right.depth() == CV_16S) && (disparity_map_right.channels() == 1) ); + CV_Assert( (disparity_map_left.cols() == disparity_map_right.cols()) ); + CV_Assert( (disparity_map_left.rows() == disparity_map_right.rows()) ); + computeConfidenceMap(disparity_map_left,disparity_map_right,ROI); + Mat disp_full_size = disparity_map_left.getMat(); + Mat src_full_size = left_view.getMat(); + if(disp_full_size.size!=src_full_size.size) + { + float x_ratio = src_full_size.cols/(float)disp_full_size.cols; + float y_ratio = src_full_size.rows/(float)disp_full_size.rows; + resize(disp_full_size,disp_full_size,src_full_size.size()); + disp_full_size = disp_full_size*x_ratio; + resize(confidence_map,confidence_map,src_full_size.size()); + ROI = Rect((int)(ROI.x*x_ratio),(int)(ROI.y*y_ratio),(int)(ROI.width*x_ratio),(int)(ROI.height*y_ratio)); + } + disp = Mat(disp_full_size,ROI); + src = Mat(src_full_size ,ROI); + filtered_disparity_map.create(disp_full_size.size(), disp_full_size.type()); + Mat& dst_full_size = filtered_disparity_map.getMatRef(); + dst_full_size = Scalar(-16); + dst = Mat(dst_full_size,ROI); + Mat conf(confidence_map,ROI); - Mat disp_32F,dst_32F; - disp.convertTo(disp_32F,CV_32F); - guidedFilter(src,disp_32F,dst_32F,radius,eps); - dst_32F.convertTo(dst,CV_16S); + Mat disp_mul_conf; + disp.convertTo(disp_mul_conf,CV_32F); + disp_mul_conf = conf.mul(disp_mul_conf); + Mat conf_filtered; + Ptr wls = createFastGlobalSmootherFilter(src,lambda,sigma_color); + wls->filter(disp_mul_conf,disp_mul_conf); + wls->filter(conf,conf_filtered); + disp_mul_conf = disp_mul_conf.mul(1/(conf_filtered+EPS)); + disp_mul_conf.convertTo(dst,CV_16S); } -}; +} -CV_EXPORTS_W -Ptr createDisparityGuidedFilter() +DisparityWLSFilterImpl::ComputeDiscontinuityAwareLRC_ParBody::ComputeDiscontinuityAwareLRC_ParBody(DisparityWLSFilterImpl& _wls, Mat& _left_disp, Mat& _right_disp, Mat& _left_disc, Mat& _right_disc, Mat& _dst, Rect _left_ROI, Rect _right_ROI, int _nstripes): +wls(&_wls),left_disp(&_left_disp),right_disp(&_right_disp),left_disc(&_left_disc),right_disc(&_right_disc),dst(&_dst),left_ROI(_left_ROI),right_ROI(_right_ROI),nstripes(_nstripes) { - return Ptr(DisparityGuidedFilterImpl::create()); + stripe_sz = (int)ceil(left_disp->rows/(double)nstripes); } - -////////////////////////////////////////////////////////////////////////////////////////////////////// -class DisparityWLSFilterImpl : public DisparityWLSFilter +void DisparityWLSFilterImpl::ComputeDiscontinuityAwareLRC_ParBody::operator() (const Range& range) const { -protected: - double lambda,sigma_color; - void init(double _lambda, double _sigma_color) + short* row_left; + float* row_left_conf; + short* row_right; + float* row_right_conf; + float* row_dst; + int right_idx; + int h = left_disp->rows; + + int start = std::min(range.start * stripe_sz, h); + int end = std::min(range.end * stripe_sz, h); + int thresh = (int)(wls->resize_factor*wls->LRC_thresh); + for(int i=start;iptr(i); + row_left_conf = (float*)left_disc->ptr(i); + row_right = (short*)right_disp->ptr(i); + row_right_conf = (float*)right_disc->ptr(i); + row_dst = (float*)dst->ptr(i); + int j_start = left_ROI.x; + int j_end = left_ROI.x+left_ROI.width; + int right_end = right_ROI.x+right_ROI.width; + for(int j=j_start;j>4); + if( right_idx>=right_ROI.x && right_idxrows/(double)nstripes); +} - static Ptr create() +void DisparityWLSFilterImpl::ComputeDepthDisc_ParBody::operator() (const Range& range) const +{ + float* row_disp; + float* row_disp_squares; + float* row_dst; + float variance; + int h = disp->rows; + int w = disp->cols; + int start = std::min(range.start * stripe_sz, h); + int end = std::min(range.end * stripe_sz, h); + float roll_off = wls->depth_discontinuity_roll_off_factor/(wls->resize_factor*wls->resize_factor); + + for(int i=start;iinit(500.0,2.0); - return Ptr(wls); + row_disp = (float*)disp->ptr(i); + row_disp_squares = (float*)disp_squares->ptr(i); + row_dst = (float*)dst->ptr(i); + + for(int j=0;j _ops, vector& _src, vector& _dst): +wls(&_wls),ops(_ops),src(_src),dst(_dst) +{} + +void DisparityWLSFilterImpl::ParallelMatOp_ParBody::operator() (const Range& range) const +{ + for(int i=range.start;i*ops[i])(*src[i],*dst[i]); +} + +CV_EXPORTS_W +Ptr createDisparityWLSFilter(bool use_confidence) +{ + return Ptr(DisparityWLSFilterImpl::create(use_confidence)); +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#define UNKNOWN_DISPARITY 16320 - void filter(InputArray disparity_map, InputArray left_view, OutputArray filtered_disparity_map,Rect ROI) +int readGT(String src_path,OutputArray dst) +{ + Mat src = imread(src_path,IMREAD_UNCHANGED); + dst.create(src.rows,src.cols,CV_16S); + Mat& dstMat = dst.getMatRef(); + + if(!src.empty() && src.channels()==3 && src.depth()==CV_8U) { - Mat disp,src,dst; - setROI(disparity_map,left_view,filtered_disparity_map,disp,src,dst,ROI); - weightedLeastSquaresFilter(src,disp,dst,lambda,sigma_color); + //MPI-Sintel format: + for(int i=0;i(i, j); + dstMat.at(i,j) = 64*bgrPixel.val[2]+bgrPixel.val[1]/4; //16-multiplied disparity + } + return 0; } -}; + else if(!src.empty() && src.channels()==1 && src.depth()==CV_8U) + { + //Middlebury format: + for(int i=0;i(i, j); + if(src_val==0) + dstMat.at(i,j) = UNKNOWN_DISPARITY; + else + dstMat.at(i,j) = 16*src_val; //16-multiplied disparity + } + return 0; + } + else + return 1; +} -CV_EXPORTS_W -Ptr createDisparityWLSFilter() +double computeMSE(InputArray GT, InputArray src, Rect ROI) { - return Ptr(DisparityWLSFilterImpl::create()); + CV_Assert( !GT.empty() && (GT.depth() == CV_16S) && (GT.channels() == 1) ); + CV_Assert( !src.empty() && (src.depth() == CV_16S) && (src.channels() == 1) ); + CV_Assert( src.rows() == GT.rows() && src.cols() == GT.cols() ); + double res = 0; + Mat GT_ROI (GT.getMat(), ROI); + Mat src_ROI(src.getMat(),ROI); + int cnt=0; + for(int i=0;i(i,j)!=UNKNOWN_DISPARITY) + { + res += (GT_ROI.at(i,j) - src_ROI.at(i,j))*(GT_ROI.at(i,j) - src_ROI.at(i,j)); + cnt++; + } + } + res /= cnt*256; + return res; } -////////////////////////////////////////////////////////////////////////////////////////////////////// +double computeBadPixelPercent(InputArray GT, InputArray src, Rect ROI, int thresh) +{ + CV_Assert( !GT.empty() && (GT.depth() == CV_16S) && (GT.channels() == 1) ); + CV_Assert( !src.empty() && (src.depth() == CV_16S) && (src.channels() == 1) ); + CV_Assert( src.rows() == GT.rows() && src.cols() == GT.cols() ); + int bad_pixel_num = 0; + Mat GT_ROI (GT.getMat(), ROI); + Mat src_ROI(src.getMat(),ROI); + int cnt=0; + for(int i=0;i(i,j)!=UNKNOWN_DISPARITY) + { + if( abs(GT_ROI.at(i,j) - src_ROI.at(i,j))>=thresh ) + bad_pixel_num++; + cnt++; + } + } + return (100.0*bad_pixel_num)/cnt; +} -void setROI(InputArray disparity_map, InputArray left_view, OutputArray filtered_disparity_map, - Mat& disp,Mat& src,Mat& dst,Rect ROI) +void getDisparityVis(InputArray src,OutputArray dst,double scale) { - Mat disp_full_size = disparity_map.getMat(); - Mat src_full_size = left_view.getMat(); - CV_Assert( (disp_full_size.depth() == CV_16S) && (disp_full_size.channels() == 1) ); - CV_Assert( (src_full_size.depth() == CV_8U) && (src_full_size.channels() <= 4) ); - disp = Mat(disp_full_size,ROI); - src = Mat(src_full_size ,ROI); - filtered_disparity_map.create(disp_full_size.size(), disp_full_size.type()); - Mat& dst_full_size = filtered_disparity_map.getMatRef(); - dst = Mat(dst_full_size,ROI); + CV_Assert( !src.empty() && (src.depth() == CV_16S) && (src.channels() == 1) ); + Mat srcMat = src.getMat(); + dst.create(srcMat.rows,srcMat.cols,CV_8UC1); + Mat& dstMat = dst.getMatRef(); + + for(int i=0;i(i,j)==UNKNOWN_DISPARITY) + dstMat.at(i,j) = 0; + else + dstMat.at(i,j) = saturate_cast(scale*srcMat.at(i,j)/16.0); + } } } diff --git a/modules/ximgproc/src/fgs_filter.cpp b/modules/ximgproc/src/fgs_filter.cpp new file mode 100644 index 000000000..e89eb8f9b --- /dev/null +++ b/modules/ximgproc/src/fgs_filter.cpp @@ -0,0 +1,694 @@ +/* + * 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 "opencv2/hal/intrin.hpp" +#include + +namespace cv { +namespace ximgproc { + +using std::vector; + +typedef float WorkType; +typedef Vec WorkVec; +typedef WorkType (*get_weight_op)(WorkType*, unsigned char*,unsigned char*); + +inline WorkType get_weight_1channel(WorkType* LUT, unsigned char* p1,unsigned char* p2) +{ + return LUT[ (p1[0]-p2[0])*(p1[0]-p2[0]) ]; +} +inline WorkType get_weight_3channel(WorkType* LUT, unsigned char* p1,unsigned char* p2) +{ + return LUT[ (p1[0]-p2[0])*(p1[0]-p2[0])+ + (p1[1]-p2[1])*(p1[1]-p2[1])+ + (p1[2]-p2[2])*(p1[2]-p2[2]) ]; +} + +class FastGlobalSmootherFilterImpl : public FastGlobalSmootherFilter +{ +public: + static Ptr create(InputArray guide, double lambda, double sigma_color, int num_iter,double lambda_attenuation); + void filter(InputArray src, OutputArray dst); + +protected: + int w,h; + int num_stripes; + float sigmaColor,lambda; + float lambda_attenuation; + int num_iter; + Mat weights_LUT; + Mat Chor, Cvert; + Mat interD; + void init(InputArray guide,double _lambda,double _sigmaColor,int _num_iter,double _lambda_attenuation); + void horizontalPass(Mat& cur); + void verticalPass(Mat& cur); +protected: + struct HorizontalPass_ParBody : public ParallelLoopBody + { + FastGlobalSmootherFilterImpl* fgs; + Mat* cur; + int nstripes, stripe_sz; + int h; + + HorizontalPass_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _cur, int _nstripes, int _h); + void operator () (const Range& range) const; + }; + inline void process_4row_block(Mat* cur,int i); + inline void process_row(Mat* cur,int i); + + struct VerticalPass_ParBody : public ParallelLoopBody + { + FastGlobalSmootherFilterImpl* fgs; + Mat* cur; + int nstripes, stripe_sz; + int w; + + VerticalPass_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _cur, int _nstripes, int _w); + void operator () (const Range& range) const; + }; + + template + struct ComputeHorizontalWeights_ParBody : public ParallelLoopBody + { + FastGlobalSmootherFilterImpl* fgs; + Mat* guide; + int nstripes, stripe_sz; + int h; + + ComputeHorizontalWeights_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _guide, int _nstripes, int _h); + void operator () (const Range& range) const; + }; + + template + struct ComputeVerticalWeights_ParBody : public ParallelLoopBody + { + FastGlobalSmootherFilterImpl* fgs; + Mat* guide; + int nstripes, stripe_sz; + int w; + + ComputeVerticalWeights_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _guide, int _nstripes, int _w); + void operator () (const Range& range) const; + }; + + struct ComputeLUT_ParBody : public ParallelLoopBody + { + FastGlobalSmootherFilterImpl* fgs; + WorkType* LUT; + int nstripes, stripe_sz; + int sz; + + ComputeLUT_ParBody(FastGlobalSmootherFilterImpl &_fgs, WorkType* _LUT, int _nstripes, int _sz); + void operator () (const Range& range) const; + }; +}; + + +void FastGlobalSmootherFilterImpl::init(InputArray guide,double _lambda,double _sigmaColor,int _num_iter,double _lambda_attenuation) +{ + CV_Assert( !guide.empty() && _lambda >= 0 && _sigmaColor >= 0 && _num_iter >=1 ); + CV_Assert( guide.depth() == CV_8U && (guide.channels() == 1 || guide.channels() == 3) ); + sigmaColor = (float)_sigmaColor; + lambda = (float)_lambda; + lambda_attenuation = (float)_lambda_attenuation; + num_iter = _num_iter; + num_stripes = getNumThreads(); + int num_levels = 3*256*256; + weights_LUT.create(1,num_levels,WorkVec::type); + + WorkType* LUT = (WorkType*)weights_LUT.ptr(0); + parallel_for_(Range(0,num_stripes),ComputeLUT_ParBody(*this,LUT,num_stripes,num_levels)); + + w = guide.cols(); + h = guide.rows(); + Chor. create(h,w,WorkVec::type); + Cvert. create(h,w,WorkVec::type); + interD.create(h,w,WorkVec::type); + Mat guideMat = guide.getMat(); + + if(guide.channels() == 1) + { + parallel_for_(Range(0,num_stripes),ComputeHorizontalWeights_ParBody(*this,guideMat,num_stripes,h)); + parallel_for_(Range(0,num_stripes),ComputeVerticalWeights_ParBody (*this,guideMat,num_stripes,w)); + } + if(guide.channels() == 3) + { + parallel_for_(Range(0,num_stripes),ComputeHorizontalWeights_ParBody(*this,guideMat,num_stripes,h)); + parallel_for_(Range(0,num_stripes),ComputeVerticalWeights_ParBody (*this,guideMat,num_stripes,w)); + } +} + +Ptr FastGlobalSmootherFilterImpl::create(InputArray guide, double lambda, double sigma_color, int num_iter, double lambda_attenuation) +{ + FastGlobalSmootherFilterImpl *fgs = new FastGlobalSmootherFilterImpl(); + fgs->init(guide,lambda,sigma_color,num_iter,lambda_attenuation); + return Ptr(fgs); +} + +void FastGlobalSmootherFilterImpl::filter(InputArray src, OutputArray dst) +{ + CV_Assert(!src.empty() && (src.depth() == CV_8U || src.depth() == CV_16S || src.depth() == CV_32F) && src.channels()<=4); + if (src.rows() != h || src.cols() != w) + { + CV_Error(Error::StsBadSize, "Size of the filtered image must be equal to the size of the guide image"); + return; + } + + vector src_channels; + vector dst_channels; + if(src.channels()==1) + src_channels.push_back(src.getMat()); + else + split(src,src_channels); + + float lambda_ref = lambda; + + for(int i=0;iptr (i); + + WorkType *Chor_row_next = (WorkType*)Chor.ptr (i+1); + WorkType *interD_row_next = (WorkType*)interD.ptr(i+1); + WorkType *cur_row_next = (WorkType*)cur->ptr (i+1); + + WorkType *Chor_row_next2 = (WorkType*)Chor.ptr (i+2); + WorkType *interD_row_next2 = (WorkType*)interD.ptr(i+2); + WorkType *cur_row_next2 = (WorkType*)cur->ptr (i+2); + + WorkType *Chor_row_next3 = (WorkType*)Chor.ptr (i+3); + WorkType *interD_row_next3 = (WorkType*)interD.ptr(i+3); + WorkType *cur_row_next3 = (WorkType*)cur->ptr (i+3); + + float coef_cur, coef_prev; + float coef_cur_row_next, coef_prev_row_next; + float coef_cur_row_next2,coef_prev_row_next2; + float coef_cur_row_next3,coef_prev_row_next3; + + //forward pass: + coef_prev = lambda*Chor_row[0]; + coef_prev_row_next = lambda*Chor_row_next[0]; + coef_prev_row_next2 = lambda*Chor_row_next2[0]; + coef_prev_row_next3 = lambda*Chor_row_next3[0]; + + interD_row[0] = coef_prev /(1-coef_prev); + interD_row_next[0] = coef_prev_row_next /(1-coef_prev_row_next); + interD_row_next2[0] = coef_prev_row_next2/(1-coef_prev_row_next2); + interD_row_next3[0] = coef_prev_row_next3/(1-coef_prev_row_next3); + + cur_row[0] = cur_row[0] /(1-coef_prev); + cur_row_next[0] = cur_row_next[0] /(1-coef_prev_row_next); + cur_row_next2[0] = cur_row_next2[0]/(1-coef_prev_row_next2); + cur_row_next3[0] = cur_row_next3[0]/(1-coef_prev_row_next3); + int j=1; + +#if CV_SIMD128 + { + v_float32x4 coef_prev_reg(coef_prev,coef_prev_row_next,coef_prev_row_next2,coef_prev_row_next3); + v_float32x4 interD_prev_reg(interD_row[0],interD_row_next[0],interD_row_next2[0],interD_row_next3[0]); + v_float32x4 cur_prev_reg(cur_row[0],cur_row_next[0],cur_row_next2[0],cur_row_next3[0]); + v_float32x4 lambda_reg(lambda,lambda,lambda,lambda); + v_float32x4 one_reg(1.0f,1.0f,1.0f,1.0f); + + v_float32x4 a0,a1,a2,a3; + v_float32x4 b0,b1,b2,b3; + v_float32x4 aux0,aux1,aux2,aux3; + +#define PROC4(Chor_in,cur_in,coef_prev_in,interD_prev_in,cur_prev_in,interD_out,cur_out,coef_cur_out)\ + coef_cur_out = lambda_reg*Chor_in;\ + aux0 = interD_prev_in*coef_prev_in;\ + aux1 = coef_cur_out+coef_prev_in;\ + aux1 = one_reg-aux1;\ + aux0 = aux1-aux0;\ + interD_out = coef_cur_out/aux0;\ + aux1 = cur_prev_in*coef_prev_in;\ + aux1 = cur_in - aux1;\ + cur_out = aux1/aux0; + + for(;j=0;j-=4) + { + //process 4x4 block: + + aux0 = v_load(interD_row+j); + aux1 = v_load(interD_row_next+j); + aux2 = v_load(interD_row_next2+j); + aux3 = v_load(interD_row_next3+j); + v_transpose4x4(aux0,aux1,aux2,aux3,a0,a1,a2,a3); + + aux0 = v_load(cur_row+j); + aux1 = v_load(cur_row_next+j); + aux2 = v_load(cur_row_next2+j); + aux3 = v_load(cur_row_next3+j); + v_transpose4x4(aux0,aux1,aux2,aux3,b0,b1,b2,b3); + + aux0 = a3*cur_next_reg; + b3 = b3-aux0; + aux0 = a2*b3; + b2 = b2-aux0; + aux0 = a1*b2; + b1 = b1-aux0; + aux0 = a0*b1; + b0 = b0-aux0; + + cur_next_reg = b0; + + v_transpose4x4(b0,b1,b2,b3,aux0,aux1,aux2,aux3); + v_store(cur_row+j,aux0); + v_store(cur_row_next+j,aux1); + v_store(cur_row_next2+j,aux2); + v_store(cur_row_next3+j,aux3); + } + j+=3; + } +#endif + + for(;j>=0;j--) + { + cur_row[j] = cur_row[j] -interD_row[j] *cur_row[j+1]; + cur_row_next[j] = cur_row_next[j] -interD_row_next[j] *cur_row_next[j+1]; + cur_row_next2[j] = cur_row_next2[j]-interD_row_next2[j]*cur_row_next2[j+1]; + cur_row_next3[j] = cur_row_next3[j]-interD_row_next3[j]*cur_row_next3[j+1]; + } +} + +void FastGlobalSmootherFilterImpl::process_row(Mat* cur,int i) +{ + WorkType denom; + WorkType *Chor_row = (WorkType*)Chor.ptr(i); + WorkType *interD_row = (WorkType*)interD.ptr(i); + WorkType *cur_row = (WorkType*)cur->ptr(i); + + float coef_cur,coef_prev; + + //forward pass: + coef_prev = lambda*Chor_row[0]; + interD_row[0] = coef_prev/(1-coef_prev); + cur_row[0] = cur_row[0]/(1-coef_prev); + for(int j=1;j=0;j--) + cur_row[j] = cur_row[j]-interD_row[j]*cur_row[j+1]; +} + +void FastGlobalSmootherFilterImpl::HorizontalPass_ParBody::operator()(const Range& range) const +{ + int start = std::min(range.start * stripe_sz, h); + int end = std::min(range.end * stripe_sz, h); + + int i=start; + for(;iprocess_4row_block(cur,i); + for(;iprocess_row(cur,i); +} + +FastGlobalSmootherFilterImpl::VerticalPass_ParBody::VerticalPass_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _cur, int _nstripes, int _w): +fgs(&_fgs),cur(&_cur), nstripes(_nstripes), w(_w) +{ + stripe_sz = (int)ceil(w/(double)nstripes); +} + +void FastGlobalSmootherFilterImpl::VerticalPass_ParBody::operator()(const Range& range) const +{ + int start = std::min(range.start * stripe_sz, w); + int end = std::min(range.end * stripe_sz, w); + + //float lambda = fgs->lambda; + WorkType denom; + WorkType *Cvert_row, *Cvert_row_prev; + WorkType *interD_row, *interD_row_prev, *cur_row, *cur_row_prev, *cur_row_next; + + float coef_cur,coef_prev; + + Cvert_row = (WorkType*)fgs->Cvert.ptr(0); + interD_row = (WorkType*)fgs->interD.ptr(0); + cur_row = (WorkType*)cur->ptr(0); + //forward pass: + for(int j=start;jlambda*Cvert_row[j]; + interD_row[j] = coef_cur/(1-coef_cur); + cur_row[j] = cur_row[j]/(1-coef_cur); + } + for(int i=1;ih;i++) + { + Cvert_row = (WorkType*)fgs->Cvert.ptr(i); + Cvert_row_prev = (WorkType*)fgs->Cvert.ptr(i-1); + interD_row = (WorkType*)fgs->interD.ptr(i); + interD_row_prev = (WorkType*)fgs->interD.ptr(i-1); + cur_row = (WorkType*)cur->ptr(i); + cur_row_prev = (WorkType*)cur->ptr(i-1); + int j = start; + +#if CV_SIMD128 + v_float32x4 a,b,c,d,coef_cur_reg,coef_prev_reg; + v_float32x4 one_reg(1.0f,1.0f,1.0f,1.0f); + v_float32x4 lambda_reg(fgs->lambda,fgs->lambda,fgs->lambda,fgs->lambda); + int sz4 = 4*((end-start)/4); + int end4 = start+sz4; + for(;jlambda*Cvert_row_prev[j]; + coef_cur = fgs->lambda*Cvert_row[j]; + denom = (1-coef_prev-coef_cur)-interD_row_prev[j]*coef_prev; + interD_row[j] = coef_cur/denom; + cur_row[j] = (cur_row[j]-cur_row_prev[j]*coef_prev)/denom; + } + } + + //backward pass: + for(int i=fgs->h-2;i>=0;i--) + { + interD_row = (WorkType*)fgs->interD.ptr(i); + cur_row = (WorkType*)cur->ptr(i); + cur_row_next = (WorkType*)cur->ptr(i+1); + int j = start; +#if CV_SIMD128 + v_float32x4 a,b; + int sz4 = 4*((end-start)/4); + int end4 = start+sz4; + for(;j +FastGlobalSmootherFilterImpl::ComputeHorizontalWeights_ParBody::ComputeHorizontalWeights_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _guide, int _nstripes, int _h): +fgs(&_fgs),guide(&_guide), nstripes(_nstripes), h(_h) +{ + stripe_sz = (int)ceil(h/(double)nstripes); +} + +template +void FastGlobalSmootherFilterImpl::ComputeHorizontalWeights_ParBody::operator()(const Range& range) const +{ + int start = std::min(range.start * stripe_sz, h); + int end = std::min(range.end * stripe_sz, h); + + WorkType* LUT = (WorkType*)fgs->weights_LUT.ptr(0); + unsigned char *row; + WorkType *Chor_row; + + for(int i=start;iptr(i); + Chor_row = (WorkType*)fgs->Chor.ptr(i); + Chor_row[0] = get_weight(LUT,row,row+num_ch); + row+=num_ch; + for(int j=1;jw-1;j++) + { + Chor_row[j] = get_weight(LUT,row,row+num_ch); + row+=num_ch; + } + Chor_row[fgs->w-1]=0; + } +} + +template +FastGlobalSmootherFilterImpl::ComputeVerticalWeights_ParBody::ComputeVerticalWeights_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _guide, int _nstripes, int _w): +fgs(&_fgs),guide(&_guide), nstripes(_nstripes), w(_w) +{ + stripe_sz = (int)ceil(w/(double)nstripes); +} + +template +void FastGlobalSmootherFilterImpl::ComputeVerticalWeights_ParBody::operator()(const Range& range) const +{ + int start = std::min(range.start * stripe_sz, w); + int end = std::min(range.end * stripe_sz, w); + + WorkType* LUT = (WorkType*)fgs->weights_LUT.ptr(0); + unsigned char *row,*row_next; + WorkType *Cvert_row; + + Cvert_row = (WorkType*)fgs->Cvert.ptr(0); + row = guide->ptr(0)+start*num_ch; + row_next = guide->ptr(1)+start*num_ch; + for(int j=start;jh-1;i++) + { + row = guide->ptr(i)+start*num_ch; + row_next = guide->ptr(i+1)+start*num_ch; + Cvert_row = (WorkType*)fgs->Cvert.ptr(i); + for(int j=start;jCvert.ptr(fgs->h-1); + for(int j=start;jsigmaColor)); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////// + +CV_EXPORTS_W +Ptr createFastGlobalSmootherFilter(InputArray guide, double lambda, double sigma_color, double lambda_attenuation, int num_iter) +{ + return Ptr(FastGlobalSmootherFilterImpl::create(guide, lambda, sigma_color, num_iter, lambda_attenuation)); +} + +CV_EXPORTS_W +void fastGlobalSmootherFilter(InputArray guide, InputArray src, OutputArray dst, double lambda, double sigma_color, double lambda_attenuation, int num_iter) +{ + Ptr fgs = createFastGlobalSmootherFilter(guide, lambda, sigma_color, lambda_attenuation, num_iter); + fgs->filter(src, dst); +} + +} +} diff --git a/modules/ximgproc/src/wls_filter.cpp b/modules/ximgproc/src/wls_filter.cpp deleted file mode 100644 index 61c400592..000000000 --- a/modules/ximgproc/src/wls_filter.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* - * 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" - -namespace cv { -namespace ximgproc { - -class WeightedLeastSquaresFilterImpl : public WeightedLeastSquaresFilter -{ -public: - static Ptr create(InputArray guide, double lambda, double sigma_color, int num_iter); - void filter(InputArray src, OutputArray dst); - ~WeightedLeastSquaresFilterImpl(); -protected: - int w,h; - double sigmaColor,lambda; - int num_iter; - double *weights_LUT; - double *Ahor, *Bhor, *Chor, - *Avert, *Bvert, *Cvert; - double *interD,*interE,*cur_res; - void init(InputArray guide,double _lambda,double _sigmaColor,int _num_iter); - void buildCoefMatrices(Mat& guide); - void horizontalPass(double* cur); - void verticalPass(double* cur); -}; - -void WeightedLeastSquaresFilterImpl::init(InputArray guide,double _lambda,double _sigmaColor,int _num_iter) -{ - //currently support only 3 channel 8bit images as guides - CV_Assert( !guide.empty() && _lambda >= 0 && _sigmaColor >= 0 && _num_iter >=1 ); - CV_Assert( guide.depth() == CV_8U && guide.channels() == 3 ); - sigmaColor = _sigmaColor; - lambda = _lambda; - num_iter = _num_iter; - int num_levels = 3*256*256; - weights_LUT = new double[num_levels]; - for(int i=0;i WeightedLeastSquaresFilterImpl::create(InputArray guide, double lambda, double sigma_color, int num_iter) -{ - WeightedLeastSquaresFilterImpl *wls = new WeightedLeastSquaresFilterImpl(); - wls->init(guide,lambda,sigma_color,num_iter); - return Ptr(wls); -} - -WeightedLeastSquaresFilter::~WeightedLeastSquaresFilter(){} -WeightedLeastSquaresFilterImpl::~WeightedLeastSquaresFilterImpl() -{ - delete[] weights_LUT; - delete[] Ahor; delete[] Bhor; delete[] Chor; - delete[] Avert; delete[] Bvert; delete[] Cvert; - delete[] interD;delete[] interE;delete[] cur_res; -} - -void WeightedLeastSquaresFilterImpl::buildCoefMatrices(Mat& guide) -{ - double hor_weight; - const unsigned char *row,*row_prev,*row_next; - for(int i=0;i(cur_res[i*w+j]); - row++; - } - } -} - -void WeightedLeastSquaresFilterImpl::horizontalPass(double* cur) -{ - double denom; - for(int i=0;i=0;j--) - cur[i*w+j] = interE[i*w+j]-interD[i*w+j]*cur[i*w+j+1]; - } -} - -void WeightedLeastSquaresFilterImpl::verticalPass(double* cur) -{ - double denom; - //forward pass: - for(int j=0;j=0;i--) - for(int j=w-1;j>=0;j--) - cur[i*w+j] = interE[i*w+j]-interD[i*w+j]*cur[(i+1)*w+j]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -CV_EXPORTS_W -Ptr createWeightedLeastSquaresFilter(InputArray guide, double lambda, double sigma_color, int num_iter) -{ - return Ptr(WeightedLeastSquaresFilterImpl::create(guide, lambda, sigma_color, num_iter)); -} - -CV_EXPORTS_W -void weightedLeastSquaresFilter(InputArray guide, InputArray src, OutputArray dst, double lambda, double sigma_color, int num_iter) -{ - Ptr wls = createWeightedLeastSquaresFilter(guide, lambda, sigma_color, num_iter); - wls->filter(src, dst); -} - -} -} diff --git a/modules/ximgproc/test/test_disparity_wls_filter.cpp b/modules/ximgproc/test/test_disparity_wls_filter.cpp new file mode 100644 index 000000000..0170a9002 --- /dev/null +++ b/modules/ximgproc/test/test_disparity_wls_filter.cpp @@ -0,0 +1,154 @@ +/* + * 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" +#include "opencv2/ximgproc/disparity_filter.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(); +} + +CV_ENUM(SrcTypes, CV_16S); +CV_ENUM(GuideTypes, CV_8UC1, CV_8UC3) +typedef tuple DisparityWLSParams; +typedef TestWithParam DisparityWLSFilterTest; + +TEST(DisparityWLSFilterTest, ReferenceAccuracy) +{ + string dir = getDataDir() + "cv/disparityfilter"; + + Mat left = imread(dir + "/left_view.png",IMREAD_COLOR); + ASSERT_FALSE(left.empty()); + Mat left_disp = imread(dir + "/disparity_left_raw.png",IMREAD_GRAYSCALE); + ASSERT_FALSE(left_disp.empty()); + left_disp.convertTo(left_disp,CV_16S,16); + Mat right_disp = imread(dir + "/disparity_right_raw.png",IMREAD_GRAYSCALE); + ASSERT_FALSE(right_disp.empty()); + right_disp.convertTo(right_disp,CV_16S,-16); + + Mat GT; + ASSERT_FALSE(readGT(dir + "/GT.png",GT)); + + FileStorage ROI_storage( dir + "/ROI.xml", FileStorage::READ ); + Rect ROI((int)ROI_storage["x"],(int)ROI_storage["y"],(int)ROI_storage["width"],(int)ROI_storage["height"]); + + FileStorage reference_res( dir + "/reference_accuracy.xml", FileStorage::READ ); + double ref_MSE = (double)reference_res["MSE_after"]; + double ref_BadPercent = (double)reference_res["BadPercent_after"]; + + cv::setNumThreads(cv::getNumberOfCPUs()); + Mat res; + + Ptr wls_filter = createDisparityWLSFilter(true); + wls_filter->setLambda(8000.0); + wls_filter->setSigmaColor(0.5); + wls_filter->filter(left_disp,left,res,ROI,right_disp); + + double MSE = computeMSE(GT,res,ROI); + double BadPercent = computeBadPixelPercent(GT,res,ROI); + double eps = 0.01; + + EXPECT_LE(MSE,ref_MSE+eps*ref_MSE); + EXPECT_LE(BadPercent,ref_BadPercent+eps*ref_BadPercent); +} + +TEST_P(DisparityWLSFilterTest, MultiThreadReproducibility) +{ + if (cv::getNumberOfCPUs() == 1) + return; + + double MAX_DIF = 1.0; + double MAX_MEAN_DIF = 1.0 / 256.0; + int loopsCount = 2; + RNG rng(0); + + DisparityWLSParams params = GetParam(); + Size size = get<0>(params); + int srcType = get<1>(params); + int guideType = get<2>(params); + bool use_conf = get<3>(params); + bool use_downscale = get<4>(params); + + Mat left(size, guideType); + randu(left, 0, 255); + Mat left_disp(size,srcType); + int max_disp = (int)(size.width*0.1); + randu(left_disp, 0, max_disp-1); + Mat right_disp(size,srcType); + randu(left_disp, -max_disp+1, 0); + Rect ROI(max_disp,0,size.width-max_disp,size.height); + + if(use_downscale) + { + resize(left_disp,left_disp,Size(),0.5,0.5); + resize(right_disp,right_disp,Size(),0.5,0.5); + ROI = Rect(ROI.x/2,ROI.y/2,ROI.width/2,ROI.height/2); + } + + for (int iter = 0; iter <= loopsCount; iter++) + { + double lambda = rng.uniform(100.0, 10000.0); + double sigma = rng.uniform(1.0, 100.0); + + Ptr wls_filter = createDisparityWLSFilter(use_conf); + wls_filter->setLambda(lambda); + wls_filter->setSigmaColor(sigma); + + cv::setNumThreads(cv::getNumberOfCPUs()); + Mat resMultiThread; + wls_filter->filter(left_disp,left,resMultiThread,ROI,right_disp); + + cv::setNumThreads(1); + Mat resSingleThread; + wls_filter->filter(left_disp,left,resSingleThread,ROI,right_disp); + + EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_INF), MAX_DIF); + EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_L1), MAX_MEAN_DIF*left.total()); + } +} +INSTANTIATE_TEST_CASE_P(FullSet,DisparityWLSFilterTest,Combine(Values(szODD, szQVGA), SrcTypes::all(), GuideTypes::all(),Values(true,false),Values(true,false))); +} \ No newline at end of file diff --git a/modules/ximgproc/test/test_fgs_filter.cpp b/modules/ximgproc/test/test_fgs_filter.cpp new file mode 100644 index 000000000..7b48cff0b --- /dev/null +++ b/modules/ximgproc/test/test_fgs_filter.cpp @@ -0,0 +1,153 @@ +/* + * 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(); +} + +CV_ENUM(SrcTypes, CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4, CV_16SC1, CV_16SC2, CV_16SC3, CV_16SC4, CV_32FC1, CV_32FC2, CV_32FC3, CV_32FC4); +CV_ENUM(GuideTypes, CV_8UC1, CV_8UC3) +typedef tuple FGSParams; +typedef TestWithParam FastGlobalSmootherTest; + +TEST(FastGlobalSmootherTest, SplatSurfaceAccuracy) +{ + RNG rnd(0); + + for (int i = 0; i < 10; i++) + { + Size sz(rnd.uniform(512, 1024), rnd.uniform(512, 1024)); + + int guideCn = rnd.uniform(1, 2); + if(guideCn==2) guideCn++; //1 or 3 channels + Mat guide(sz, 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(sz, CV_MAKE_TYPE(CV_16S, srcCn), surfaceValue); + + double lambda = rnd.uniform(100, 10000); + double sigma = rnd.uniform(1.0, 100.0); + + Mat res; + fastGlobalSmootherFilter(guide, src, res, lambda, sigma); + + // When filtering a constant image we should get the same image: + double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels(); + EXPECT_LE(normL1, 1.0/64); + } +} + +TEST(FastGlobalSmootherTest, 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; + fastGlobalSmootherFilter(src,src,res,1000.0,10.0); + + double totalMaxError = 1.0/64.0*src.total()*src.channels(); + + EXPECT_LE(cvtest::norm(res, ref, NORM_L2), totalMaxError); + EXPECT_LE(cvtest::norm(res, ref, NORM_INF), 1); +} + +TEST_P(FastGlobalSmootherTest, MultiThreadReproducibility) +{ + if (cv::getNumberOfCPUs() == 1) + return; + + double MAX_DIF = 1.0; + double MAX_MEAN_DIF = 1.0 / 64.0; + int loopsCount = 2; + RNG rng(0); + + FGSParams params = GetParam(); + Size size = get<0>(params); + int srcType = get<1>(params); + int guideType = get<2>(params); + + Mat guide(size, guideType); + randu(guide, 0, 255); + Mat src(size,srcType); + 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++) + { + double lambda = rng.uniform(100.0, 10000.0); + double sigma = rng.uniform(1.0, 100.0); + + cv::setNumThreads(cv::getNumberOfCPUs()); + Mat resMultiThread; + fastGlobalSmootherFilter(guide, src, resMultiThread, lambda, sigma); + + cv::setNumThreads(1); + Mat resSingleThread; + fastGlobalSmootherFilter(guide, src, resSingleThread, lambda, sigma); + + 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(FullSet, FastGlobalSmootherTest,Combine(Values(szODD, szQVGA), SrcTypes::all(), GuideTypes::all())); + +} \ No newline at end of file diff --git a/modules/ximgproc/testdata/disparityfilter/GT.png b/modules/ximgproc/testdata/disparityfilter/GT.png new file mode 100644 index 000000000..4be979bf5 Binary files /dev/null and b/modules/ximgproc/testdata/disparityfilter/GT.png differ diff --git a/modules/ximgproc/testdata/disparityfilter/ROI.xml b/modules/ximgproc/testdata/disparityfilter/ROI.xml new file mode 100644 index 000000000..ab1e4b006 --- /dev/null +++ b/modules/ximgproc/testdata/disparityfilter/ROI.xml @@ -0,0 +1,7 @@ + + +166 +7 +851 +422 + diff --git a/modules/ximgproc/testdata/disparityfilter/disparity_left_raw.png b/modules/ximgproc/testdata/disparityfilter/disparity_left_raw.png new file mode 100644 index 000000000..3fb78c714 Binary files /dev/null and b/modules/ximgproc/testdata/disparityfilter/disparity_left_raw.png differ diff --git a/modules/ximgproc/testdata/disparityfilter/disparity_right_raw.png b/modules/ximgproc/testdata/disparityfilter/disparity_right_raw.png new file mode 100644 index 000000000..e8d9fb168 Binary files /dev/null and b/modules/ximgproc/testdata/disparityfilter/disparity_right_raw.png differ diff --git a/modules/ximgproc/testdata/disparityfilter/left_view.png b/modules/ximgproc/testdata/disparityfilter/left_view.png new file mode 100644 index 000000000..9cbdcf86d Binary files /dev/null and b/modules/ximgproc/testdata/disparityfilter/left_view.png differ diff --git a/modules/ximgproc/testdata/disparityfilter/reference_accuracy.xml b/modules/ximgproc/testdata/disparityfilter/reference_accuracy.xml new file mode 100644 index 000000000..aa8614709 --- /dev/null +++ b/modules/ximgproc/testdata/disparityfilter/reference_accuracy.xml @@ -0,0 +1,7 @@ + + +5043.6495 +128.3773 +48.5417 +45.8749 + diff --git a/modules/ximgproc/testdata/edgefilter/fgs/kodim23_lambda=1000_sigma=10.png b/modules/ximgproc/testdata/edgefilter/fgs/kodim23_lambda=1000_sigma=10.png new file mode 100644 index 000000000..bb715802d Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/fgs/kodim23_lambda=1000_sigma=10.png differ