Skip to content

Commit a70af9f

Browse files
committed
Adding edge-aware disparity filtering
Added basic interface and demo for disparity filtering, added unoptimized fast weighted least squares filter implementation. Current demo tests domain transform, guided and weighted least squares filters on a dataset, measures speed and quality.
1 parent 7b6fe5c commit a70af9f

File tree

6 files changed

+831
-1
lines changed

6 files changed

+831
-1
lines changed

modules/ximgproc/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
set(the_description "Extended image processing module. It includes edge-aware filters and etc.")
22
set(OPENCV_MODULE_IS_PART_OF_WORLD OFF)
3-
ocv_define_module(ximgproc opencv_imgproc opencv_core opencv_highgui WRAP python)
3+
ocv_define_module(ximgproc opencv_imgproc opencv_core opencv_highgui opencv_calib3d WRAP python)
44

55
target_link_libraries(opencv_ximgproc)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* By downloading, copying, installing or using the software you agree to this license.
3+
* If you do not agree to this license, do not download, install,
4+
* copy or use the software.
5+
*
6+
*
7+
* License Agreement
8+
* For Open Source Computer Vision Library
9+
* (3 - clause BSD License)
10+
*
11+
* Redistribution and use in source and binary forms, with or without modification,
12+
* are permitted provided that the following conditions are met :
13+
*
14+
* *Redistributions of source code must retain the above copyright notice,
15+
* this list of conditions and the following disclaimer.
16+
*
17+
* * Redistributions in binary form must reproduce the above copyright notice,
18+
* this list of conditions and the following disclaimer in the documentation
19+
* and / or other materials provided with the distribution.
20+
*
21+
* * Neither the names of the copyright holders nor the names of the contributors
22+
* may be used to endorse or promote products derived from this software
23+
* without specific prior written permission.
24+
*
25+
* This software is provided by the copyright holders and contributors "as is" and
26+
* any express or implied warranties, including, but not limited to, the implied
27+
* warranties of merchantability and fitness for a particular purpose are disclaimed.
28+
* In no event shall copyright holders or contributors be liable for any direct,
29+
* indirect, incidental, special, exemplary, or consequential damages
30+
* (including, but not limited to, procurement of substitute goods or services;
31+
* loss of use, data, or profits; or business interruption) however caused
32+
* and on any theory of liability, whether in contract, strict liability,
33+
* or tort(including negligence or otherwise) arising in any way out of
34+
* the use of this software, even if advised of the possibility of such damage.
35+
*/
36+
37+
#ifndef __OPENCV_DISPARITYFILTER_HPP__
38+
#define __OPENCV_DISPARITYFILTER_HPP__
39+
#ifdef __cplusplus
40+
41+
#include <opencv2/core.hpp>
42+
43+
namespace cv {
44+
namespace ximgproc {
45+
46+
class CV_EXPORTS_W DisparityFilter : public Algorithm
47+
{
48+
public:
49+
CV_WRAP virtual void filter(InputArray disparity_map, InputArray left_view, OutputArray filtered_disparity_map,Rect ROI) = 0;
50+
};
51+
52+
/////////////////////////////////////////////////////////////////////////////////////////////////
53+
54+
class CV_EXPORTS_W DisparityDTFilter : public DisparityFilter
55+
{
56+
public:
57+
CV_WRAP virtual double getSigmaSpatial() = 0;
58+
CV_WRAP virtual void setSigmaSpatial(double _sigmaSpatial) = 0;
59+
CV_WRAP virtual double getSigmaColor() = 0;
60+
CV_WRAP virtual void setSigmaColor(double _sigmaColor) = 0;
61+
};
62+
63+
CV_EXPORTS_W
64+
Ptr<DisparityDTFilter> createDisparityDTFilter();
65+
66+
class CV_EXPORTS_W DisparityGuidedFilter : public DisparityFilter
67+
{
68+
public:
69+
CV_WRAP virtual double getEps() = 0;
70+
CV_WRAP virtual void setEps(double _eps) = 0;
71+
CV_WRAP virtual int getRadius() = 0;
72+
CV_WRAP virtual void setRadius(int _radius) = 0;
73+
};
74+
75+
CV_EXPORTS_W
76+
Ptr<DisparityGuidedFilter> createDisparityGuidedFilter();
77+
78+
class CV_EXPORTS_W DisparityWLSFilter : public DisparityFilter
79+
{
80+
CV_WRAP virtual double getLambda() = 0;
81+
CV_WRAP virtual void setLambda(double _lambda) = 0;
82+
CV_WRAP virtual double getSigmaColor() = 0;
83+
CV_WRAP virtual void setSigmaColor(double _sigma_color) = 0;
84+
};
85+
86+
CV_EXPORTS_W
87+
Ptr<DisparityWLSFilter> createDisparityWLSFilter();
88+
89+
}
90+
}
91+
#endif
92+
#endif

modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,17 @@ void jointBilateralFilter(InputArray joint, InputArray src, OutputArray dst, int
318318

319319
//! @}
320320

321+
class CV_EXPORTS_W WeightedLeastSquaresFilter : public Algorithm
322+
{
323+
public:
324+
CV_WRAP virtual void filter(InputArray src, OutputArray dst) = 0;
325+
virtual ~WeightedLeastSquaresFilter();
326+
};
327+
328+
CV_EXPORTS_W Ptr<WeightedLeastSquaresFilter> createWeightedLeastSquaresFilter(InputArray guide, double lambda, double sigma_color, int num_iter=3);
329+
330+
CV_EXPORTS_W void weightedLeastSquaresFilter(InputArray guide, InputArray src, OutputArray dst, double lambda, double sigma_color, int num_iter=3);
331+
321332
}
322333
}
323334
#endif
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
#include "opencv2/calib3d.hpp"
2+
#include "opencv2/imgproc.hpp"
3+
#include "opencv2/imgcodecs.hpp"
4+
#include "opencv2/highgui.hpp"
5+
#include "opencv2/core/utility.hpp"
6+
#include "opencv2/ximgproc/disparity_filter.hpp"
7+
#include <stdio.h>
8+
#include <string>
9+
#include <vector>
10+
#include <map>
11+
12+
#if defined(_WIN32)
13+
#include <direct.h>
14+
#else
15+
#include <sys/stat.h>
16+
#endif
17+
18+
using namespace cv;
19+
using namespace cv::ximgproc;
20+
using namespace std;
21+
22+
#define UNKNOWN_DISPARITY 16320
23+
24+
static void print_help()
25+
{
26+
printf("\nDemo for disparity filtering, evaluating speed and performance of different filters\n");
27+
printf("\nUsage: disparity_filtering.exe <path_to_dataset_folder> <path_to_results_folder>\n");
28+
}
29+
30+
struct dataset_entry
31+
{
32+
string name;
33+
string dataset_folder;
34+
string left_file,right_file,GT_file;
35+
dataset_entry(string _dataset_folder): dataset_folder(_dataset_folder){}
36+
void readEntry(Mat& dst_left,Mat& dst_right,Mat& dst_GT)
37+
{
38+
dst_left = imread(dataset_folder+"/"+left_file, IMREAD_COLOR);
39+
dst_right = imread(dataset_folder+"/"+right_file, IMREAD_COLOR);
40+
Mat raw_disp = imread(dataset_folder+"/"+GT_file, IMREAD_COLOR);
41+
dst_GT = Mat(raw_disp.rows,raw_disp.cols,CV_16S);
42+
for(int i=0;i<raw_disp.rows;i++)
43+
for(int j=0;j<raw_disp.cols;j++)
44+
{
45+
Vec3b bgrPixel = raw_disp.at<Vec3b>(i, j);
46+
dst_GT.at<short>(i,j) = 64*bgrPixel.val[2]+bgrPixel.val[1]/4; //16-multiplied disparity
47+
}
48+
}
49+
};
50+
51+
struct config
52+
{
53+
Ptr<StereoMatcher> matcher_instance;
54+
Ptr<DisparityFilter> filter_instance;
55+
config(Ptr<StereoMatcher> _matcher_instance,Ptr<DisparityFilter> _filter_instance)
56+
{
57+
matcher_instance = _matcher_instance;
58+
filter_instance = _filter_instance;
59+
}
60+
config() {}
61+
};
62+
63+
void operator>>(const FileNode& node,dataset_entry& entry);
64+
double computeMSE(Mat& GT, Mat& src, Rect ROI);
65+
double computeBadPixelPercent(Mat& GT, Mat& src, Rect ROI, int thresh=24/*1.5 pixels*/);
66+
void getDisparityVis(Mat& disparity_map,Mat& dst);
67+
Rect computeROI(Size2i src_sz, Ptr<StereoMatcher> matcher_instance);
68+
void setConfigsForTesting(map<string,config>& cfgs);
69+
void CreateDir(string path);
70+
71+
int main(int argc, char** argv)
72+
{
73+
if(argc < 3)
74+
{
75+
print_help();
76+
return 0;
77+
}
78+
string dataset_folder(argv[1]);
79+
string res_folder(argv[2]);
80+
81+
map<string,config> configs_for_testing;
82+
setConfigsForTesting(configs_for_testing);
83+
CreateDir(res_folder);
84+
85+
for (map<string,config>::iterator cfg = configs_for_testing.begin(); cfg != configs_for_testing.end(); cfg++)
86+
{
87+
string vis_folder = res_folder+"/vis_"+cfg->first;
88+
CreateDir(vis_folder);
89+
90+
string cfg_file_name = res_folder+"/"+cfg->first+"_res.csv";
91+
FILE* cur_cfg_res_file = fopen(cfg_file_name.c_str(),"w");
92+
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");
93+
94+
printf("Processing configuration: %s\n",cfg->first.c_str());
95+
96+
FileStorage fs(dataset_folder + "/_dataset.xml", FileStorage::READ);
97+
FileNode n = fs["data_set"];
98+
double MSE_pre,percent_pre,MSE_post,percent_post,matching_time,filtering_time;
99+
double average_MSE_pre=0,average_percent_pre=0,average_MSE_post=0,
100+
average_percent_post=0,average_matching_time=0,average_filtering_time=0;
101+
int cnt = 0;
102+
for (FileNodeIterator it = n.begin(); it != n.end(); it++)
103+
{
104+
dataset_entry entry(dataset_folder);
105+
(*it)>>entry;
106+
printf("%s ",entry.name.c_str());
107+
Mat left,right,GT;
108+
entry.readEntry(left,right,GT);
109+
Mat raw_disp;
110+
Mat left_gray; cvtColor(left, left_gray, COLOR_BGR2GRAY );
111+
Mat right_gray; cvtColor(right, right_gray, COLOR_BGR2GRAY );
112+
matching_time = (double)getTickCount();
113+
cfg->second.matcher_instance->compute(left_gray,right_gray,raw_disp);
114+
matching_time = ((double)getTickCount() - matching_time)/getTickFrequency();
115+
116+
Rect ROI = computeROI(left.size(),cfg->second.matcher_instance);
117+
Mat filtered_disp;
118+
filtering_time = (double)getTickCount();
119+
cfg->second.filter_instance->filter(raw_disp,left,filtered_disp,ROI);
120+
filtering_time = ((double)getTickCount() - filtering_time)/getTickFrequency();
121+
122+
123+
MSE_pre = computeMSE(GT,raw_disp,ROI);
124+
percent_pre = computeBadPixelPercent(GT,raw_disp,ROI);
125+
MSE_post = computeMSE(GT,filtered_disp,ROI);
126+
percent_post = computeBadPixelPercent(GT,filtered_disp,ROI);
127+
128+
fprintf(cur_cfg_res_file,"%s,%.1f,%.1f,%.1f,%.1f,%.3f,%.3f\n",entry.name.c_str(),MSE_pre,MSE_post,
129+
percent_pre,percent_post,matching_time,filtering_time);
130+
131+
average_matching_time+=matching_time; average_filtering_time+=filtering_time;
132+
average_MSE_pre+=MSE_pre; average_percent_pre+=percent_pre;
133+
average_MSE_post+=MSE_post; average_percent_post+=percent_post;
134+
cnt++;
135+
136+
// dump visualizations:
137+
imwrite(vis_folder + "/" + entry.name + "_left.png",left);
138+
Mat GT_vis,raw_disp_vis,filtered_disp_vis;
139+
getDisparityVis(GT,GT_vis);
140+
getDisparityVis(raw_disp,raw_disp_vis);
141+
getDisparityVis(filtered_disp,filtered_disp_vis);
142+
imwrite(vis_folder + "/" + entry.name + "_disparity_GT.png",GT_vis);
143+
imwrite(vis_folder + "/" + entry.name + "_disparity_raw.png",raw_disp_vis);
144+
imwrite(vis_folder + "/" + entry.name + "_disparity_filtered.png",filtered_disp_vis);
145+
146+
printf("- Done\n");
147+
148+
}
149+
fprintf(cur_cfg_res_file,"%s,%.1f,%.1f,%.1f,%.1f,%.3f,%.3f\n","average",average_MSE_pre/cnt,
150+
average_MSE_post/cnt,average_percent_pre/cnt,average_percent_post/cnt,
151+
average_matching_time/cnt,average_filtering_time/cnt);
152+
fclose(cur_cfg_res_file);
153+
}
154+
return 0;
155+
}
156+
157+
void operator>>(const FileNode& node,dataset_entry& entry)
158+
{
159+
node["name"] >> entry.name;
160+
node["left_file"] >> entry.left_file;
161+
node["right_file"] >> entry.right_file;
162+
node["GT_file"] >> entry.GT_file;
163+
}
164+
165+
double computeMSE(Mat& GT, Mat& src, Rect ROI)
166+
{
167+
double res = 0;
168+
Mat GT_ROI(GT,ROI);
169+
Mat src_ROI(src,ROI);
170+
int cnt=0;
171+
for(int i=0;i<src_ROI.rows;i++)
172+
for(int j=0;j<src_ROI.cols;j++)
173+
{
174+
if(GT_ROI.at<short>(i,j)!=UNKNOWN_DISPARITY)
175+
{
176+
res += (GT_ROI.at<short>(i,j) - src_ROI.at<short>(i,j))*(GT_ROI.at<short>(i,j) - src_ROI.at<short>(i,j));
177+
cnt++;
178+
}
179+
}
180+
res /= cnt*256;
181+
return res;
182+
}
183+
184+
double computeBadPixelPercent(Mat& GT, Mat& src, Rect ROI, int thresh)
185+
{
186+
int bad_pixel_num = 0;
187+
Mat GT_ROI(GT,ROI);
188+
Mat src_ROI(src,ROI);
189+
int cnt=0;
190+
for(int i=0;i<src_ROI.rows;i++)
191+
for(int j=0;j<src_ROI.cols;j++)
192+
{
193+
if(GT_ROI.at<short>(i,j)!=UNKNOWN_DISPARITY)
194+
{
195+
if( abs(GT_ROI.at<short>(i,j) - src_ROI.at<short>(i,j))>=thresh )
196+
bad_pixel_num++;
197+
cnt++;
198+
}
199+
}
200+
return (100.0*bad_pixel_num)/cnt;
201+
}
202+
203+
void getDisparityVis(Mat& disparity_map,Mat& dst)
204+
{
205+
dst = Mat(disparity_map.rows,disparity_map.cols,CV_8UC3);
206+
for(int i=0;i<dst.rows;i++)
207+
for(int j=0;j<dst.cols;j++)
208+
{
209+
if(disparity_map.at<short>(i,j)==UNKNOWN_DISPARITY)
210+
dst.at<Vec3b>(i,j) = Vec3b(0,0,0);
211+
else
212+
dst.at<Vec3b>(i,j) = Vec3b(saturate_cast<unsigned char>(disparity_map.at<short>(i,j)/8),
213+
saturate_cast<unsigned char>(disparity_map.at<short>(i,j)/8),
214+
saturate_cast<unsigned char>(disparity_map.at<short>(i,j)/8));
215+
}
216+
}
217+
218+
Rect computeROI(Size2i src_sz, Ptr<StereoMatcher> matcher_instance)
219+
{
220+
int min_disparity = matcher_instance->getMinDisparity();
221+
int num_disparities = matcher_instance->getNumDisparities();
222+
int block_size = matcher_instance->getBlockSize();
223+
224+
int bs2 = block_size/2;
225+
int minD = min_disparity, maxD = min_disparity + num_disparities - 1;
226+
227+
int xmin = maxD + bs2;
228+
int xmax = src_sz.width - minD - bs2;
229+
int ymin = bs2;
230+
int ymax = src_sz.height - bs2;
231+
232+
Rect r(xmin, ymin, xmax - xmin, ymax - ymin);
233+
return r;
234+
}
235+
236+
void setConfigsForTesting(map<string,config>& cfgs)
237+
{
238+
Ptr<StereoBM> stereobm_matcher = StereoBM::create(128,21);
239+
stereobm_matcher->setTextureThreshold(0);
240+
stereobm_matcher->setUniquenessRatio(0);
241+
242+
Ptr<DisparityFilter> wls_filter = createDisparityWLSFilter();
243+
Ptr<DisparityFilter> dt_filter = createDisparityDTFilter();
244+
Ptr<DisparityFilter> guided_filter = createDisparityGuidedFilter();
245+
246+
cfgs["stereobm_wls"] = config(stereobm_matcher,wls_filter);
247+
cfgs["stereobm_dtf"] = config(stereobm_matcher,dt_filter);
248+
cfgs["stereobm_gf"] = config(stereobm_matcher,guided_filter);
249+
}
250+
251+
void CreateDir(string path)
252+
{
253+
#if defined(_WIN32)
254+
_mkdir(path.c_str());
255+
#else
256+
mkdir(path.c_str(), 0777);
257+
#endif
258+
}

0 commit comments

Comments
 (0)