1 #include <iostream>
2 #include <string>
3 #include <sstream>
4 #include <iomanip>
5 #include <stdexcept>
6 #include <opencv2/core/utility.hpp>
7 #include "opencv2/cudastereo.hpp"
8 #include "opencv2/highgui.hpp"
9 #include "opencv2/imgproc.hpp"
10 
11 using namespace cv;
12 using namespace std;
13 
14 bool help_showed = false;
15 
16 struct Params
17 {
18     Params();
19     static Params read(int argc, char** argv);
20 
21     string left;
22     string right;
23 
method_strParams24     string method_str() const
25     {
26         switch (method)
27         {
28         case BM: return "BM";
29         case BP: return "BP";
30         case CSBP: return "CSBP";
31         }
32         return "";
33     }
34     enum {BM, BP, CSBP} method;
35     int ndisp; // Max disparity + 1
36 };
37 
38 
39 struct App
40 {
41     App(const Params& p);
42     void run();
43     void handleKey(char key);
44     void printParams() const;
45 
workBeginApp46     void workBegin() { work_begin = getTickCount(); }
workEndApp47     void workEnd()
48     {
49         int64 d = getTickCount() - work_begin;
50         double f = getTickFrequency();
51         work_fps = f / d;
52     }
53 
textApp54     string text() const
55     {
56         stringstream ss;
57         ss << "(" << p.method_str() << ") FPS: " << setiosflags(ios::left)
58             << setprecision(4) << work_fps;
59         return ss.str();
60     }
61 private:
62     Params p;
63     bool running;
64 
65     Mat left_src, right_src;
66     Mat left, right;
67     cuda::GpuMat d_left, d_right;
68 
69     Ptr<cuda::StereoBM> bm;
70     Ptr<cuda::StereoBeliefPropagation> bp;
71     Ptr<cuda::StereoConstantSpaceBP> csbp;
72 
73     int64 work_begin;
74     double work_fps;
75 };
76 
printHelp()77 static void printHelp()
78 {
79     cout << "Usage: stereo_match\n"
80         << "\t--left <left_view> --right <right_view> # must be rectified\n"
81         << "\t--method <stereo_match_method> # BM | BP | CSBP\n"
82         << "\t--ndisp <number> # number of disparity levels\n";
83     help_showed = true;
84 }
85 
main(int argc,char ** argv)86 int main(int argc, char** argv)
87 {
88     try
89     {
90         if (argc < 2)
91         {
92             printHelp();
93             return 1;
94         }
95         Params args = Params::read(argc, argv);
96         if (help_showed)
97             return -1;
98         App app(args);
99         app.run();
100     }
101     catch (const exception& e)
102     {
103         cout << "error: " << e.what() << endl;
104     }
105     return 0;
106 }
107 
108 
Params()109 Params::Params()
110 {
111     method = BM;
112     ndisp = 64;
113 }
114 
115 
read(int argc,char ** argv)116 Params Params::read(int argc, char** argv)
117 {
118     Params p;
119 
120     for (int i = 1; i < argc; i++)
121     {
122         if (string(argv[i]) == "--left") p.left = argv[++i];
123         else if (string(argv[i]) == "--right") p.right = argv[++i];
124         else if (string(argv[i]) == "--method")
125         {
126             if (string(argv[i + 1]) == "BM") p.method = BM;
127             else if (string(argv[i + 1]) == "BP") p.method = BP;
128             else if (string(argv[i + 1]) == "CSBP") p.method = CSBP;
129             else throw runtime_error("unknown stereo match method: " + string(argv[i + 1]));
130             i++;
131         }
132         else if (string(argv[i]) == "--ndisp") p.ndisp = atoi(argv[++i]);
133         else if (string(argv[i]) == "--help") printHelp();
134         else throw runtime_error("unknown key: " + string(argv[i]));
135     }
136 
137     return p;
138 }
139 
140 
App(const Params & params)141 App::App(const Params& params)
142     : p(params), running(false)
143 {
144     cv::cuda::printShortCudaDeviceInfo(cv::cuda::getDevice());
145 
146     cout << "stereo_match_gpu sample\n";
147     cout << "\nControls:\n"
148         << "\tesc - exit\n"
149         << "\tp - print current parameters\n"
150         << "\tg - convert source images into gray\n"
151         << "\tm - change stereo match method\n"
152         << "\ts - change Sobel prefiltering flag (for BM only)\n"
153         << "\t1/q - increase/decrease maximum disparity\n"
154         << "\t2/w - increase/decrease window size (for BM only)\n"
155         << "\t3/e - increase/decrease iteration count (for BP and CSBP only)\n"
156         << "\t4/r - increase/decrease level count (for BP and CSBP only)\n";
157 }
158 
159 
run()160 void App::run()
161 {
162     // Load images
163     left_src = imread(p.left);
164     right_src = imread(p.right);
165     if (left_src.empty()) throw runtime_error("can't open file \"" + p.left + "\"");
166     if (right_src.empty()) throw runtime_error("can't open file \"" + p.right + "\"");
167     cvtColor(left_src, left, COLOR_BGR2GRAY);
168     cvtColor(right_src, right, COLOR_BGR2GRAY);
169     d_left.upload(left);
170     d_right.upload(right);
171 
172     imshow("left", left);
173     imshow("right", right);
174 
175     // Set common parameters
176     bm = cuda::createStereoBM(p.ndisp);
177     bp = cuda::createStereoBeliefPropagation(p.ndisp);
178     csbp = cv::cuda::createStereoConstantSpaceBP(p.ndisp);
179 
180     // Prepare disparity map of specified type
181     Mat disp(left.size(), CV_8U);
182     cuda::GpuMat d_disp(left.size(), CV_8U);
183 
184     cout << endl;
185     printParams();
186 
187     running = true;
188     while (running)
189     {
190         workBegin();
191         switch (p.method)
192         {
193         case Params::BM:
194             if (d_left.channels() > 1 || d_right.channels() > 1)
195             {
196                 cout << "BM doesn't support color images\n";
197                 cvtColor(left_src, left, COLOR_BGR2GRAY);
198                 cvtColor(right_src, right, COLOR_BGR2GRAY);
199                 cout << "image_channels: " << left.channels() << endl;
200                 d_left.upload(left);
201                 d_right.upload(right);
202                 imshow("left", left);
203                 imshow("right", right);
204             }
205             bm->compute(d_left, d_right, d_disp);
206             break;
207         case Params::BP: bp->compute(d_left, d_right, d_disp); break;
208         case Params::CSBP: csbp->compute(d_left, d_right, d_disp); break;
209         }
210         workEnd();
211 
212         // Show results
213         d_disp.download(disp);
214         putText(disp, text(), Point(5, 25), FONT_HERSHEY_SIMPLEX, 1.0, Scalar::all(255));
215         imshow("disparity", (Mat_<uchar>)disp);
216 
217         handleKey((char)waitKey(3));
218     }
219 }
220 
221 
printParams() const222 void App::printParams() const
223 {
224     cout << "--- Parameters ---\n";
225     cout << "image_size: (" << left.cols << ", " << left.rows << ")\n";
226     cout << "image_channels: " << left.channels() << endl;
227     cout << "method: " << p.method_str() << endl
228         << "ndisp: " << p.ndisp << endl;
229     switch (p.method)
230     {
231     case Params::BM:
232         cout << "win_size: " << bm->getBlockSize() << endl;
233         cout << "prefilter_sobel: " << bm->getPreFilterType() << endl;
234         break;
235     case Params::BP:
236         cout << "iter_count: " << bp->getNumIters() << endl;
237         cout << "level_count: " << bp->getNumLevels() << endl;
238         break;
239     case Params::CSBP:
240         cout << "iter_count: " << csbp->getNumIters() << endl;
241         cout << "level_count: " << csbp->getNumLevels() << endl;
242         break;
243     }
244     cout << endl;
245 }
246 
247 
handleKey(char key)248 void App::handleKey(char key)
249 {
250     switch (key)
251     {
252     case 27:
253         running = false;
254         break;
255     case 'p': case 'P':
256         printParams();
257         break;
258     case 'g': case 'G':
259         if (left.channels() == 1 && p.method != Params::BM)
260         {
261             left = left_src;
262             right = right_src;
263         }
264         else
265         {
266             cvtColor(left_src, left, COLOR_BGR2GRAY);
267             cvtColor(right_src, right, COLOR_BGR2GRAY);
268         }
269         d_left.upload(left);
270         d_right.upload(right);
271         cout << "image_channels: " << left.channels() << endl;
272         imshow("left", left);
273         imshow("right", right);
274         break;
275     case 'm': case 'M':
276         switch (p.method)
277         {
278         case Params::BM:
279             p.method = Params::BP;
280             break;
281         case Params::BP:
282             p.method = Params::CSBP;
283             break;
284         case Params::CSBP:
285             p.method = Params::BM;
286             break;
287         }
288         cout << "method: " << p.method_str() << endl;
289         break;
290     case 's': case 'S':
291         if (p.method == Params::BM)
292         {
293             switch (bm->getPreFilterType())
294             {
295             case 0:
296                 bm->setPreFilterType(cv::StereoBM::PREFILTER_XSOBEL);
297                 break;
298             case cv::StereoBM::PREFILTER_XSOBEL:
299                 bm->setPreFilterType(0);
300                 break;
301             }
302             cout << "prefilter_sobel: " << bm->getPreFilterType() << endl;
303         }
304         break;
305     case '1':
306         p.ndisp = p.ndisp == 1 ? 8 : p.ndisp + 8;
307         cout << "ndisp: " << p.ndisp << endl;
308         bm->setNumDisparities(p.ndisp);
309         bp->setNumDisparities(p.ndisp);
310         csbp->setNumDisparities(p.ndisp);
311         break;
312     case 'q': case 'Q':
313         p.ndisp = max(p.ndisp - 8, 1);
314         cout << "ndisp: " << p.ndisp << endl;
315         bm->setNumDisparities(p.ndisp);
316         bp->setNumDisparities(p.ndisp);
317         csbp->setNumDisparities(p.ndisp);
318         break;
319     case '2':
320         if (p.method == Params::BM)
321         {
322             bm->setBlockSize(min(bm->getBlockSize() + 1, 51));
323             cout << "win_size: " << bm->getBlockSize() << endl;
324         }
325         break;
326     case 'w': case 'W':
327         if (p.method == Params::BM)
328         {
329             bm->setBlockSize(max(bm->getBlockSize() - 1, 2));
330             cout << "win_size: " << bm->getBlockSize() << endl;
331         }
332         break;
333     case '3':
334         if (p.method == Params::BP)
335         {
336             bp->setNumIters(bp->getNumIters() + 1);
337             cout << "iter_count: " << bp->getNumIters() << endl;
338         }
339         else if (p.method == Params::CSBP)
340         {
341             csbp->setNumIters(csbp->getNumIters() + 1);
342             cout << "iter_count: " << csbp->getNumIters() << endl;
343         }
344         break;
345     case 'e': case 'E':
346         if (p.method == Params::BP)
347         {
348             bp->setNumIters(max(bp->getNumIters() - 1, 1));
349             cout << "iter_count: " << bp->getNumIters() << endl;
350         }
351         else if (p.method == Params::CSBP)
352         {
353             csbp->setNumIters(max(csbp->getNumIters() - 1, 1));
354             cout << "iter_count: " << csbp->getNumIters() << endl;
355         }
356         break;
357     case '4':
358         if (p.method == Params::BP)
359         {
360             bp->setNumLevels(bp->getNumLevels() + 1);
361             cout << "level_count: " << bp->getNumLevels() << endl;
362         }
363         else if (p.method == Params::CSBP)
364         {
365             csbp->setNumLevels(csbp->getNumLevels() + 1);
366             cout << "level_count: " << csbp->getNumLevels() << endl;
367         }
368         break;
369     case 'r': case 'R':
370         if (p.method == Params::BP)
371         {
372             bp->setNumLevels(max(bp->getNumLevels() - 1, 1));
373             cout << "level_count: " << bp->getNumLevels() << endl;
374         }
375         else if (p.method == Params::CSBP)
376         {
377             csbp->setNumLevels(max(csbp->getNumLevels() - 1, 1));
378             cout << "level_count: " << csbp->getNumLevels() << endl;
379         }
380         break;
381     }
382 }
383