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.hpp"
7 #include <iostream>
8 #include <string>
9
10 using namespace cv;
11 using namespace cv::ximgproc;
12 using namespace std;
13
14 Rect computeROI(Size2i src_sz, Ptr<StereoMatcher> matcher_instance);
15
16 const String keys =
17 "{help h usage ? | | print this message }"
18 "{@left |../data/aloeL.jpg | left view of the stereopair }"
19 "{@right |../data/aloeR.jpg | right view of the stereopair }"
20 "{GT |../data/aloeGT.png| optional ground-truth disparity (MPI-Sintel or Middlebury format) }"
21 "{dst_path |None | optional path to save the resulting filtered disparity map }"
22 "{dst_raw_path |None | optional path to save raw disparity map before filtering }"
23 "{algorithm |bm | stereo matching method (bm or sgbm) }"
24 "{filter |wls_conf | used post-filtering (wls_conf or wls_no_conf or fbs_conf) }"
25 "{no-display | | don't display results }"
26 "{no-downscale | | force stereo matching on full-sized views to improve quality }"
27 "{dst_conf_path |None | optional path to save the confidence map used in filtering }"
28 "{vis_mult |1.0 | coefficient used to scale disparity map visualizations }"
29 "{max_disparity |160 | parameter of stereo matching }"
30 "{window_size |-1 | parameter of stereo matching }"
31 "{wls_lambda |8000.0 | parameter of wls post-filtering }"
32 "{wls_sigma |1.5 | parameter of wls post-filtering }"
33 "{fbs_spatial |16.0 | parameter of fbs post-filtering }"
34 "{fbs_luma |8.0 | parameter of fbs post-filtering }"
35 "{fbs_chroma |8.0 | parameter of fbs post-filtering }"
36 "{fbs_lambda |128.0 | parameter of fbs post-filtering }"
37 ;
38
main(int argc,char ** argv)39 int main(int argc, char** argv)
40 {
41 CommandLineParser parser(argc,argv,keys);
42 parser.about("Disparity Filtering Demo");
43 if (parser.has("help"))
44 {
45 parser.printMessage();
46 return 0;
47 }
48
49 String left_im = parser.get<String>(0);
50 String right_im = parser.get<String>(1);
51 String GT_path = parser.get<String>("GT");
52
53 String dst_path = parser.get<String>("dst_path");
54 String dst_raw_path = parser.get<String>("dst_raw_path");
55 String dst_conf_path = parser.get<String>("dst_conf_path");
56 String algo = parser.get<String>("algorithm");
57 String filter = parser.get<String>("filter");
58 bool no_display = parser.has("no-display");
59 bool no_downscale = parser.has("no-downscale");
60 int max_disp = parser.get<int>("max_disparity");
61 double lambda = parser.get<double>("wls_lambda");
62 double sigma = parser.get<double>("wls_sigma");
63 double fbs_spatial = parser.get<double>("fbs_spatial");
64 double fbs_luma = parser.get<double>("fbs_luma");
65 double fbs_chroma = parser.get<double>("fbs_chroma");
66 double fbs_lambda = parser.get<double>("fbs_lambda");
67 double vis_mult = parser.get<double>("vis_mult");
68
69 int wsize;
70 if(parser.get<int>("window_size")>=0) //user provided window_size value
71 wsize = parser.get<int>("window_size");
72 else
73 {
74 if(algo=="sgbm")
75 wsize = 3; //default window size for SGBM
76 else if(!no_downscale && algo=="bm" && filter=="wls_conf")
77 wsize = 7; //default window size for BM on downscaled views (downscaling is performed only for wls_conf)
78 else
79 wsize = 15; //default window size for BM on full-sized views
80 }
81
82 if (!parser.check())
83 {
84 parser.printErrors();
85 return -1;
86 }
87
88 //! [load_views]
89 Mat left = imread(left_im ,IMREAD_COLOR);
90 if ( left.empty() )
91 {
92 cout<<"Cannot read image file: "<<left_im;
93 return -1;
94 }
95
96 Mat right = imread(right_im,IMREAD_COLOR);
97 if ( right.empty() )
98 {
99 cout<<"Cannot read image file: "<<right_im;
100 return -1;
101 }
102 //! [load_views]
103
104 bool noGT;
105 Mat GT_disp;
106 if (GT_path=="../data/aloeGT.png" && left_im!="../data/aloeL.jpg")
107 noGT=true;
108 else
109 {
110 noGT=false;
111 if(readGT(GT_path,GT_disp)!=0)
112 {
113 cout<<"Cannot read ground truth image file: "<<GT_path<<endl;
114 return -1;
115 }
116 }
117
118 Mat left_for_matcher, right_for_matcher;
119 Mat left_disp,right_disp;
120 Mat filtered_disp,solved_disp,solved_filtered_disp;
121 Mat conf_map = Mat(left.rows,left.cols,CV_8U);
122 conf_map = Scalar(255);
123 Rect ROI;
124 Ptr<DisparityWLSFilter> wls_filter;
125 double matching_time, filtering_time;
126 double solving_time = 0;
127 if(max_disp<=0 || max_disp%16!=0)
128 {
129 cout<<"Incorrect max_disparity value: it should be positive and divisible by 16";
130 return -1;
131 }
132 if(wsize<=0 || wsize%2!=1)
133 {
134 cout<<"Incorrect window_size value: it should be positive and odd";
135 return -1;
136 }
137
138 if(filter=="wls_conf") // filtering with confidence (significantly better quality than wls_no_conf)
139 {
140 if(!no_downscale)
141 {
142 // downscale the views to speed-up the matching stage, as we will need to compute both left
143 // and right disparity maps for confidence map computation
144 //! [downscale]
145 max_disp/=2;
146 if(max_disp%16!=0)
147 max_disp += 16-(max_disp%16);
148 resize(left ,left_for_matcher ,Size(),0.5,0.5, INTER_LINEAR_EXACT);
149 resize(right,right_for_matcher,Size(),0.5,0.5, INTER_LINEAR_EXACT);
150 //! [downscale]
151 }
152 else
153 {
154 left_for_matcher = left.clone();
155 right_for_matcher = right.clone();
156 }
157
158 if(algo=="bm")
159 {
160 //! [matching]
161 Ptr<StereoBM> left_matcher = StereoBM::create(max_disp,wsize);
162 wls_filter = createDisparityWLSFilter(left_matcher);
163 Ptr<StereoMatcher> right_matcher = createRightMatcher(left_matcher);
164
165 cvtColor(left_for_matcher, left_for_matcher, COLOR_BGR2GRAY);
166 cvtColor(right_for_matcher, right_for_matcher, COLOR_BGR2GRAY);
167
168 matching_time = (double)getTickCount();
169 left_matcher-> compute(left_for_matcher, right_for_matcher,left_disp);
170 right_matcher->compute(right_for_matcher,left_for_matcher, right_disp);
171 matching_time = ((double)getTickCount() - matching_time)/getTickFrequency();
172 //! [matching]
173 }
174 else if(algo=="sgbm")
175 {
176 Ptr<StereoSGBM> left_matcher = StereoSGBM::create(0,max_disp,wsize);
177 left_matcher->setP1(24*wsize*wsize);
178 left_matcher->setP2(96*wsize*wsize);
179 left_matcher->setPreFilterCap(63);
180 left_matcher->setMode(StereoSGBM::MODE_SGBM_3WAY);
181 wls_filter = createDisparityWLSFilter(left_matcher);
182 Ptr<StereoMatcher> right_matcher = createRightMatcher(left_matcher);
183
184 matching_time = (double)getTickCount();
185 left_matcher-> compute(left_for_matcher, right_for_matcher,left_disp);
186 right_matcher->compute(right_for_matcher,left_for_matcher, right_disp);
187 matching_time = ((double)getTickCount() - matching_time)/getTickFrequency();
188 }
189 else
190 {
191 cout<<"Unsupported algorithm";
192 return -1;
193 }
194
195 //! [filtering]
196 wls_filter->setLambda(lambda);
197 wls_filter->setSigmaColor(sigma);
198 filtering_time = (double)getTickCount();
199 wls_filter->filter(left_disp,left,filtered_disp,right_disp);
200 filtering_time = ((double)getTickCount() - filtering_time)/getTickFrequency();
201 //! [filtering]
202 conf_map = wls_filter->getConfidenceMap();
203
204 // Get the ROI that was used in the last filter call:
205 ROI = wls_filter->getROI();
206 if(!no_downscale)
207 {
208 // upscale raw disparity and ROI back for a proper comparison:
209 resize(left_disp,left_disp,Size(),2.0,2.0,INTER_LINEAR_EXACT);
210 left_disp = left_disp*2.0;
211 ROI = Rect(ROI.x*2,ROI.y*2,ROI.width*2,ROI.height*2);
212 }
213 }
214 else if(filter=="fbs_conf") // filtering with fbs and confidence using also wls pre-processing
215 {
216 if(!no_downscale)
217 {
218 // downscale the views to speed-up the matching stage, as we will need to compute both left
219 // and right disparity maps for confidence map computation
220 //! [downscale_wls]
221 max_disp/=2;
222 if(max_disp%16!=0)
223 max_disp += 16-(max_disp%16);
224 resize(left ,left_for_matcher ,Size(),0.5,0.5);
225 resize(right,right_for_matcher,Size(),0.5,0.5);
226 //! [downscale_wls]
227 }
228 else
229 {
230 left_for_matcher = left.clone();
231 right_for_matcher = right.clone();
232 }
233
234 if(algo=="bm")
235 {
236 //! [matching_wls]
237 Ptr<StereoBM> left_matcher = StereoBM::create(max_disp,wsize);
238 wls_filter = createDisparityWLSFilter(left_matcher);
239 Ptr<StereoMatcher> right_matcher = createRightMatcher(left_matcher);
240
241 cvtColor(left_for_matcher, left_for_matcher, COLOR_BGR2GRAY);
242 cvtColor(right_for_matcher, right_for_matcher, COLOR_BGR2GRAY);
243
244 matching_time = (double)getTickCount();
245 left_matcher-> compute(left_for_matcher, right_for_matcher,left_disp);
246 right_matcher->compute(right_for_matcher,left_for_matcher, right_disp);
247 matching_time = ((double)getTickCount() - matching_time)/getTickFrequency();
248 //! [matching_wls]
249 }
250 else if(algo=="sgbm")
251 {
252 Ptr<StereoSGBM> left_matcher = StereoSGBM::create(0,max_disp,wsize);
253 left_matcher->setP1(24*wsize*wsize);
254 left_matcher->setP2(96*wsize*wsize);
255 left_matcher->setPreFilterCap(63);
256 left_matcher->setMode(StereoSGBM::MODE_SGBM_3WAY);
257 wls_filter = createDisparityWLSFilter(left_matcher);
258 Ptr<StereoMatcher> right_matcher = createRightMatcher(left_matcher);
259
260 matching_time = (double)getTickCount();
261 left_matcher-> compute(left_for_matcher, right_for_matcher,left_disp);
262 right_matcher->compute(right_for_matcher,left_for_matcher, right_disp);
263 matching_time = ((double)getTickCount() - matching_time)/getTickFrequency();
264 }
265 else
266 {
267 cout<<"Unsupported algorithm";
268 return -1;
269 }
270
271 //! [filtering_wls]
272 wls_filter->setLambda(lambda);
273 wls_filter->setSigmaColor(sigma);
274 filtering_time = (double)getTickCount();
275 wls_filter->filter(left_disp,left,filtered_disp,right_disp);
276 filtering_time = ((double)getTickCount() - filtering_time)/getTickFrequency();
277 //! [filtering_wls]
278
279 conf_map = wls_filter->getConfidenceMap();
280
281 Mat left_disp_resized;
282 resize(left_disp,left_disp_resized,left.size());
283
284 // Get the ROI that was used in the last filter call:
285 ROI = wls_filter->getROI();
286 if(!no_downscale)
287 {
288 // upscale raw disparity and ROI back for a proper comparison:
289 resize(left_disp,left_disp,Size(),2.0,2.0);
290 left_disp = left_disp*2.0;
291 left_disp_resized = left_disp_resized*2.0;
292 ROI = Rect(ROI.x*2,ROI.y*2,ROI.width*2,ROI.height*2);
293 }
294
295 #ifdef HAVE_EIGEN
296 //! [filtering_fbs]
297 solving_time = (double)getTickCount();
298 fastBilateralSolverFilter(left, left_disp_resized, conf_map/255.0f, solved_disp, fbs_spatial, fbs_luma, fbs_chroma, fbs_lambda);
299 solving_time = ((double)getTickCount() - solving_time)/getTickFrequency();
300 //! [filtering_fbs]
301
302 //! [filtering_wls2fbs]
303 fastBilateralSolverFilter(left, filtered_disp, conf_map/255.0f, solved_filtered_disp, fbs_spatial, fbs_luma, fbs_chroma, fbs_lambda);
304 //! [filtering_wls2fbs]
305 #else
306 (void)fbs_spatial;
307 (void)fbs_luma;
308 (void)fbs_chroma;
309 (void)fbs_lambda;
310 #endif
311 }
312 else if(filter=="wls_no_conf")
313 {
314 /* There is no convenience function for the case of filtering with no confidence, so we
315 will need to set the ROI and matcher parameters manually */
316
317 left_for_matcher = left.clone();
318 right_for_matcher = right.clone();
319
320 if(algo=="bm")
321 {
322 Ptr<StereoBM> matcher = StereoBM::create(max_disp,wsize);
323 matcher->setTextureThreshold(0);
324 matcher->setUniquenessRatio(0);
325 cvtColor(left_for_matcher, left_for_matcher, COLOR_BGR2GRAY);
326 cvtColor(right_for_matcher, right_for_matcher, COLOR_BGR2GRAY);
327 ROI = computeROI(left_for_matcher.size(),matcher);
328 wls_filter = createDisparityWLSFilterGeneric(false);
329 wls_filter->setDepthDiscontinuityRadius((int)ceil(0.33*wsize));
330
331 matching_time = (double)getTickCount();
332 matcher->compute(left_for_matcher,right_for_matcher,left_disp);
333 matching_time = ((double)getTickCount() - matching_time)/getTickFrequency();
334 }
335 else if(algo=="sgbm")
336 {
337 Ptr<StereoSGBM> matcher = StereoSGBM::create(0,max_disp,wsize);
338 matcher->setUniquenessRatio(0);
339 matcher->setDisp12MaxDiff(1000000);
340 matcher->setSpeckleWindowSize(0);
341 matcher->setP1(24*wsize*wsize);
342 matcher->setP2(96*wsize*wsize);
343 matcher->setMode(StereoSGBM::MODE_SGBM_3WAY);
344 ROI = computeROI(left_for_matcher.size(),matcher);
345 wls_filter = createDisparityWLSFilterGeneric(false);
346 wls_filter->setDepthDiscontinuityRadius((int)ceil(0.5*wsize));
347
348 matching_time = (double)getTickCount();
349 matcher->compute(left_for_matcher,right_for_matcher,left_disp);
350 matching_time = ((double)getTickCount() - matching_time)/getTickFrequency();
351 }
352 else
353 {
354 cout<<"Unsupported algorithm";
355 return -1;
356 }
357
358 wls_filter->setLambda(lambda);
359 wls_filter->setSigmaColor(sigma);
360 filtering_time = (double)getTickCount();
361 wls_filter->filter(left_disp,left,filtered_disp,Mat(),ROI);
362 filtering_time = ((double)getTickCount() - filtering_time)/getTickFrequency();
363 }
364 else
365 {
366 cout<<"Unsupported filter";
367 return -1;
368 }
369
370 //collect and print all the stats:
371 cout.precision(2);
372 cout<<"Matching time: "<<matching_time<<"s"<<endl;
373 cout<<"Filtering time: "<<filtering_time<<"s"<<endl;
374 cout<<"Solving time: "<<solving_time<<"s"<<endl;
375 cout<<endl;
376
377 double MSE_before,percent_bad_before,MSE_after,percent_bad_after;
378 if(!noGT)
379 {
380 MSE_before = computeMSE(GT_disp,left_disp,ROI);
381 percent_bad_before = computeBadPixelPercent(GT_disp,left_disp,ROI);
382 MSE_after = computeMSE(GT_disp,filtered_disp,ROI);
383 percent_bad_after = computeBadPixelPercent(GT_disp,filtered_disp,ROI);
384
385 cout.precision(5);
386 cout<<"MSE before filtering: "<<MSE_before<<endl;
387 cout<<"MSE after filtering: "<<MSE_after<<endl;
388 cout<<endl;
389 cout.precision(3);
390 cout<<"Percent of bad pixels before filtering: "<<percent_bad_before<<endl;
391 cout<<"Percent of bad pixels after filtering: "<<percent_bad_after<<endl;
392 }
393
394 if(dst_path!="None")
395 {
396 Mat filtered_disp_vis;
397 getDisparityVis(filtered_disp,filtered_disp_vis,vis_mult);
398 imwrite(dst_path,filtered_disp_vis);
399 }
400 if(dst_raw_path!="None")
401 {
402 Mat raw_disp_vis;
403 getDisparityVis(left_disp,raw_disp_vis,vis_mult);
404 imwrite(dst_raw_path,raw_disp_vis);
405 }
406 if(dst_conf_path!="None")
407 {
408 imwrite(dst_conf_path,conf_map);
409 }
410
411 if(!no_display)
412 {
413 namedWindow("left", WINDOW_AUTOSIZE);
414 imshow("left", left);
415 namedWindow("right", WINDOW_AUTOSIZE);
416 imshow("right", right);
417
418 if(!noGT)
419 {
420 Mat GT_disp_vis;
421 getDisparityVis(GT_disp,GT_disp_vis,vis_mult);
422 namedWindow("ground-truth disparity", WINDOW_AUTOSIZE);
423 imshow("ground-truth disparity", GT_disp_vis);
424 }
425
426 //! [visualization]
427 Mat raw_disp_vis;
428 getDisparityVis(left_disp,raw_disp_vis,vis_mult);
429 namedWindow("raw disparity", WINDOW_AUTOSIZE);
430 imshow("raw disparity", raw_disp_vis);
431 Mat filtered_disp_vis;
432 getDisparityVis(filtered_disp,filtered_disp_vis,vis_mult);
433 namedWindow("filtered disparity", WINDOW_AUTOSIZE);
434 imshow("filtered disparity", filtered_disp_vis);
435
436 if(!solved_disp.empty())
437 {
438 Mat solved_disp_vis;
439 getDisparityVis(solved_disp,solved_disp_vis,vis_mult);
440 namedWindow("solved disparity", WINDOW_AUTOSIZE);
441 imshow("solved disparity", solved_disp_vis);
442
443 Mat solved_filtered_disp_vis;
444 getDisparityVis(solved_filtered_disp,solved_filtered_disp_vis,vis_mult);
445 namedWindow("solved wls disparity", WINDOW_AUTOSIZE);
446 imshow("solved wls disparity", solved_filtered_disp_vis);
447 }
448
449 while(1)
450 {
451 char key = (char)waitKey();
452 if( key == 27 || key == 'q' || key == 'Q') // 'ESC'
453 break;
454 }
455 //! [visualization]
456 }
457
458 return 0;
459 }
460
computeROI(Size2i src_sz,Ptr<StereoMatcher> matcher_instance)461 Rect computeROI(Size2i src_sz, Ptr<StereoMatcher> matcher_instance)
462 {
463 int min_disparity = matcher_instance->getMinDisparity();
464 int num_disparities = matcher_instance->getNumDisparities();
465 int block_size = matcher_instance->getBlockSize();
466
467 int bs2 = block_size/2;
468 int minD = min_disparity, maxD = min_disparity + num_disparities - 1;
469
470 int xmin = maxD + bs2;
471 int xmax = src_sz.width + minD - bs2;
472 int ymin = bs2;
473 int ymax = src_sz.height - bs2;
474
475 Rect r(xmin, ymin, xmax - xmin, ymax - ymin);
476 return r;
477 }
478