CLAHE Python bindings
[profile/ivi/opencv.git] / samples / ocl / hog.cpp
1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 #include <sstream>
5 #include <iomanip>
6 #include <stdexcept>
7 #include "opencv2/ocl/ocl.hpp"
8 #include "opencv2/highgui/highgui.hpp"
9
10 using namespace std;
11 using namespace cv;
12
13 bool help_showed = false;
14
15 class Args
16 {
17 public:
18     Args();
19     static Args read(int argc, char** argv);
20
21     string src;
22     bool src_is_video;
23     bool src_is_camera;
24     int camera_id;
25
26     bool write_video;
27     string dst_video;
28     double dst_video_fps;
29
30     bool make_gray;
31
32     bool resize_src;
33     int width, height;
34
35     double scale;
36     int nlevels;
37     int gr_threshold;
38
39     double hit_threshold;
40     bool hit_threshold_auto;
41
42     int win_width;
43     int win_stride_width, win_stride_height;
44
45     bool gamma_corr;
46 };
47
48 class App
49 {
50 public:
51     App(const Args& s);
52     void run();
53
54     void handleKey(char key);
55
56     void hogWorkBegin();
57     void hogWorkEnd();
58     string hogWorkFps() const;
59
60     void workBegin();
61     void workEnd();
62     string workFps() const;
63
64     string message() const;
65
66 // This function test if gpu_rst matches cpu_rst.
67 // If the two vectors are not equal, it will return the difference in vector size
68 // Else if will return 
69 // (total diff of each cpu and gpu rects covered pixels)/(total cpu rects covered pixels)
70     double checkRectSimilarity(Size sz, 
71                                std::vector<Rect>& cpu_rst, 
72                                std::vector<Rect>& gpu_rst);
73 private:
74     App operator=(App&);
75
76     Args args;
77     bool running;
78
79     bool use_gpu;
80     bool make_gray;
81     double scale;
82     int gr_threshold;
83     int nlevels;
84     double hit_threshold;
85     bool gamma_corr;
86
87     int64 hog_work_begin;
88     double hog_work_fps;
89
90     int64 work_begin;
91     double work_fps;
92 };
93
94 static void printHelp()
95 {
96     cout << "Histogram of Oriented Gradients descriptor and detector sample.\n"
97          << "\nUsage: hog_gpu\n"
98          << "  (<image>|--video <vide>|--camera <camera_id>) # frames source\n"
99          << "  [--make_gray <true/false>] # convert image to gray one or not\n"
100          << "  [--resize_src <true/false>] # do resize of the source image or not\n"
101          << "  [--width <int>] # resized image width\n"
102          << "  [--height <int>] # resized image height\n"
103          << "  [--hit_threshold <double>] # classifying plane distance threshold (0.0 usually)\n"
104          << "  [--scale <double>] # HOG window scale factor\n"
105          << "  [--nlevels <int>] # max number of HOG window scales\n"
106          << "  [--win_width <int>] # width of the window (48 or 64)\n"
107          << "  [--win_stride_width <int>] # distance by OX axis between neighbour wins\n"
108          << "  [--win_stride_height <int>] # distance by OY axis between neighbour wins\n"
109          << "  [--gr_threshold <int>] # merging similar rects constant\n"
110          << "  [--gamma_correct <int>] # do gamma correction or not\n"
111          << "  [--write_video <bool>] # write video or not\n"
112          << "  [--dst_video <path>] # output video path\n"
113          << "  [--dst_video_fps <double>] # output video fps\n";
114     help_showed = true;
115 }
116
117 int main(int argc, char** argv)
118 {
119     try
120     {
121         if (argc < 2)
122             printHelp();
123         Args args = Args::read(argc, argv);
124         if (help_showed)
125             return -1;
126         App app(args);
127         app.run();
128     }
129     catch (const Exception& e) { return cout << "error: "  << e.what() << endl, 1; }
130     catch (const exception& e) { return cout << "error: "  << e.what() << endl, 1; }
131     catch(...) { return cout << "unknown exception" << endl, 1; }
132     return 0;
133 }
134
135
136 Args::Args()
137 {
138     src_is_video = false;
139     src_is_camera = false;
140     camera_id = 0;
141
142     write_video = false;
143     dst_video_fps = 24.;
144
145     make_gray = false;
146
147     resize_src = false;
148     width = 640;
149     height = 480;
150
151     scale = 1.05;
152     nlevels = 13;
153     gr_threshold = 8;
154     hit_threshold = 1.4;
155     hit_threshold_auto = true;
156
157     win_width = 48;
158     win_stride_width = 8;
159     win_stride_height = 8;
160
161     gamma_corr = true;
162 }
163
164
165 Args Args::read(int argc, char** argv)
166 {
167     Args args;
168     for (int i = 1; i < argc; i++)
169     {
170         if (string(argv[i]) == "--make_gray") args.make_gray = (string(argv[++i]) == "true");
171         else if (string(argv[i]) == "--resize_src") args.resize_src = (string(argv[++i]) == "true");
172         else if (string(argv[i]) == "--width") args.width = atoi(argv[++i]);
173         else if (string(argv[i]) == "--height") args.height = atoi(argv[++i]);
174         else if (string(argv[i]) == "--hit_threshold")
175         {
176             args.hit_threshold = atof(argv[++i]);
177             args.hit_threshold_auto = false;
178         }
179         else if (string(argv[i]) == "--scale") args.scale = atof(argv[++i]);
180         else if (string(argv[i]) == "--nlevels") args.nlevels = atoi(argv[++i]);
181         else if (string(argv[i]) == "--win_width") args.win_width = atoi(argv[++i]);
182         else if (string(argv[i]) == "--win_stride_width") args.win_stride_width = atoi(argv[++i]);
183         else if (string(argv[i]) == "--win_stride_height") args.win_stride_height = atoi(argv[++i]);
184         else if (string(argv[i]) == "--gr_threshold") args.gr_threshold = atoi(argv[++i]);
185         else if (string(argv[i]) == "--gamma_correct") args.gamma_corr = (string(argv[++i]) == "true");
186         else if (string(argv[i]) == "--write_video") args.write_video = (string(argv[++i]) == "true");
187         else if (string(argv[i]) == "--dst_video") args.dst_video = argv[++i];
188         else if (string(argv[i]) == "--dst_video_fps") args.dst_video_fps = atof(argv[++i]);
189         else if (string(argv[i]) == "--help") printHelp();
190         else if (string(argv[i]) == "--video") { args.src = argv[++i]; args.src_is_video = true; }
191         else if (string(argv[i]) == "--camera") { args.camera_id = atoi(argv[++i]); args.src_is_camera = true; }
192         else if (args.src.empty()) args.src = argv[i];
193         else throw runtime_error((string("unknown key: ") + argv[i]));
194     }
195     return args;
196 }
197
198
199 App::App(const Args& s)
200 {
201     args = s;
202     cout << "\nControls:\n"
203          << "\tESC - exit\n"
204          << "\tm - change mode GPU <-> CPU\n"
205          << "\tg - convert image to gray or not\n"
206          << "\t1/q - increase/decrease HOG scale\n"
207          << "\t2/w - increase/decrease levels count\n"
208          << "\t3/e - increase/decrease HOG group threshold\n"
209          << "\t4/r - increase/decrease hit threshold\n"
210          << endl;
211
212     use_gpu = true;
213     make_gray = args.make_gray;
214     scale = args.scale;
215     gr_threshold = args.gr_threshold;
216     nlevels = args.nlevels;
217
218     if (args.hit_threshold_auto)
219         args.hit_threshold = args.win_width == 48 ? 1.4 : 0.;
220     hit_threshold = args.hit_threshold;
221
222     gamma_corr = args.gamma_corr;
223
224     if (args.win_width != 64 && args.win_width != 48)
225         args.win_width = 64;
226
227     cout << "Scale: " << scale << endl;
228     if (args.resize_src)
229         cout << "Resized source: (" << args.width << ", " << args.height << ")\n";
230     cout << "Group threshold: " << gr_threshold << endl;
231     cout << "Levels number: " << nlevels << endl;
232     cout << "Win width: " << args.win_width << endl;
233     cout << "Win stride: (" << args.win_stride_width << ", " << args.win_stride_height << ")\n";
234     cout << "Hit threshold: " << hit_threshold << endl;
235     cout << "Gamma correction: " << gamma_corr << endl;
236     cout << endl;
237 }
238
239
240 void App::run()
241 {
242     std::vector<ocl::Info> oclinfo;
243     ocl::getDevice(oclinfo);
244     running = true;
245     cv::VideoWriter video_writer;
246
247     Size win_size(args.win_width, args.win_width * 2); //(64, 128) or (48, 96)
248     Size win_stride(args.win_stride_width, args.win_stride_height);
249
250     // Create HOG descriptors and detectors here
251     vector<float> detector;
252     if (win_size == Size(64, 128))
253         detector = cv::ocl::HOGDescriptor::getPeopleDetector64x128();
254     else
255         detector = cv::ocl::HOGDescriptor::getPeopleDetector48x96();
256
257     cv::ocl::HOGDescriptor gpu_hog(win_size, Size(16, 16), Size(8, 8), Size(8, 8), 9,
258                                    cv::ocl::HOGDescriptor::DEFAULT_WIN_SIGMA, 0.2, gamma_corr,
259                                    cv::ocl::HOGDescriptor::DEFAULT_NLEVELS);
260     cv::HOGDescriptor cpu_hog(win_size, Size(16, 16), Size(8, 8), Size(8, 8), 9, 1, -1,
261                               HOGDescriptor::L2Hys, 0.2, gamma_corr, cv::HOGDescriptor::DEFAULT_NLEVELS);
262     gpu_hog.setSVMDetector(detector);
263     cpu_hog.setSVMDetector(detector);
264
265     while (running)
266     {
267         VideoCapture vc;
268         Mat frame;
269
270         if (args.src_is_video)
271         {
272             vc.open(args.src.c_str());
273             if (!vc.isOpened())
274                 throw runtime_error(string("can't open video file: " + args.src));
275             vc >> frame;
276         }
277         else if (args.src_is_camera)
278         {
279             vc.open(args.camera_id);
280             if (!vc.isOpened())
281             {
282                 stringstream msg;
283                 msg << "can't open camera: " << args.camera_id;
284                 throw runtime_error(msg.str());
285             }
286             vc >> frame;
287         }
288         else
289         {
290             frame = imread(args.src);
291             if (frame.empty())
292                 throw runtime_error(string("can't open image file: " + args.src));
293         }
294
295         Mat img_aux, img, img_to_show;
296         ocl::oclMat gpu_img;
297
298         // Iterate over all frames
299         bool verify = false;
300         while (running && !frame.empty())
301         {
302             workBegin();
303
304             // Change format of the image
305             if (make_gray) cvtColor(frame, img_aux, CV_BGR2GRAY);
306             else if (use_gpu) cvtColor(frame, img_aux, CV_BGR2BGRA);
307             else frame.copyTo(img_aux);
308
309             // Resize image
310             if (args.resize_src) resize(img_aux, img, Size(args.width, args.height));
311             else img = img_aux;
312             img_to_show = img;
313
314             gpu_hog.nlevels = nlevels;
315             cpu_hog.nlevels = nlevels;
316
317             vector<Rect> found;
318
319             // Perform HOG classification
320             hogWorkBegin();
321             if (use_gpu)
322             {
323                 gpu_img.upload(img);
324                 gpu_hog.detectMultiScale(gpu_img, found, hit_threshold, win_stride,
325                                          Size(0, 0), scale, gr_threshold);
326                 if (!verify)
327                 {
328                     // verify if GPU output same objects with CPU at 1st run
329                     verify = true;
330                     vector<Rect> ref_rst;
331                     cvtColor(img, img, CV_BGRA2BGR);
332                     cpu_hog.detectMultiScale(img, ref_rst, hit_threshold, win_stride,
333                                               Size(0, 0), scale, gr_threshold-2);
334                     double accuracy = checkRectSimilarity(img.size(), ref_rst, found);
335                     cout << "\naccuracy value: " << accuracy << endl;           
336                 } 
337            }
338             else cpu_hog.detectMultiScale(img, found, hit_threshold, win_stride,
339                                           Size(0, 0), scale, gr_threshold);
340             hogWorkEnd();
341
342             // Draw positive classified windows
343             for (size_t i = 0; i < found.size(); i++)
344             {
345                 Rect r = found[i];
346                 rectangle(img_to_show, r.tl(), r.br(), CV_RGB(0, 255, 0), 3);
347             }
348
349             if (use_gpu)
350                 putText(img_to_show, "Mode: GPU", Point(5, 25), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
351             else
352                 putText(img_to_show, "Mode: CPU", Point(5, 25), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
353             putText(img_to_show, "FPS (HOG only): " + hogWorkFps(), Point(5, 65), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
354             putText(img_to_show, "FPS (total): " + workFps(), Point(5, 105), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
355             imshow("opencv_gpu_hog", img_to_show);
356
357             if (args.src_is_video || args.src_is_camera) vc >> frame;
358
359             workEnd();
360
361             if (args.write_video)
362             {
363                 if (!video_writer.isOpened())
364                 {
365                     video_writer.open(args.dst_video, CV_FOURCC('x','v','i','d'), args.dst_video_fps,
366                                       img_to_show.size(), true);
367                     if (!video_writer.isOpened())
368                         throw std::runtime_error("can't create video writer");
369                 }
370
371                 if (make_gray) cvtColor(img_to_show, img, CV_GRAY2BGR);
372                 else cvtColor(img_to_show, img, CV_BGRA2BGR);
373
374                 video_writer << img;
375             }
376
377             handleKey((char)waitKey(3));
378         }
379     }
380 }
381
382
383 void App::handleKey(char key)
384 {
385     switch (key)
386     {
387     case 27:
388         running = false;
389         break;
390     case 'm':
391     case 'M':
392         use_gpu = !use_gpu;
393         cout << "Switched to " << (use_gpu ? "CUDA" : "CPU") << " mode\n";
394         break;
395     case 'g':
396     case 'G':
397         make_gray = !make_gray;
398         cout << "Convert image to gray: " << (make_gray ? "YES" : "NO") << endl;
399         break;
400     case '1':
401         scale *= 1.05;
402         cout << "Scale: " << scale << endl;
403         break;
404     case 'q':
405     case 'Q':
406         scale /= 1.05;
407         cout << "Scale: " << scale << endl;
408         break;
409     case '2':
410         nlevels++;
411         cout << "Levels number: " << nlevels << endl;
412         break;
413     case 'w':
414     case 'W':
415         nlevels = max(nlevels - 1, 1);
416         cout << "Levels number: " << nlevels << endl;
417         break;
418     case '3':
419         gr_threshold++;
420         cout << "Group threshold: " << gr_threshold << endl;
421         break;
422     case 'e':
423     case 'E':
424         gr_threshold = max(0, gr_threshold - 1);
425         cout << "Group threshold: " << gr_threshold << endl;
426         break;
427     case '4':
428         hit_threshold+=0.25;
429         cout << "Hit threshold: " << hit_threshold << endl;
430         break;
431     case 'r':
432     case 'R':
433         hit_threshold = max(0.0, hit_threshold - 0.25);
434         cout << "Hit threshold: " << hit_threshold << endl;
435         break;
436     case 'c':
437     case 'C':
438         gamma_corr = !gamma_corr;
439         cout << "Gamma correction: " << gamma_corr << endl;
440         break;
441     }
442 }
443
444
445 inline void App::hogWorkBegin() { hog_work_begin = getTickCount(); }
446
447 inline void App::hogWorkEnd()
448 {
449     int64 delta = getTickCount() - hog_work_begin;
450     double freq = getTickFrequency();
451     hog_work_fps = freq / delta;
452 }
453
454 inline string App::hogWorkFps() const
455 {
456     stringstream ss;
457     ss << hog_work_fps;
458     return ss.str();
459 }
460
461
462 inline void App::workBegin() { work_begin = getTickCount(); }
463
464 inline void App::workEnd()
465 {
466     int64 delta = getTickCount() - work_begin;
467     double freq = getTickFrequency();
468     work_fps = freq / delta;
469 }
470
471 inline string App::workFps() const
472 {
473     stringstream ss;
474     ss << work_fps;
475     return ss.str();
476 }
477
478 double App::checkRectSimilarity(Size sz, 
479                                 std::vector<Rect>& ob1, 
480                                 std::vector<Rect>& ob2)
481 {
482     double final_test_result = 0.0;
483     size_t sz1 = ob1.size();
484     size_t sz2 = ob2.size();
485
486     if(sz1 != sz2)
487         return sz1 > sz2 ? (double)(sz1 - sz2) : (double)(sz2 - sz1);
488     else
489     {
490         cv::Mat cpu_result(sz, CV_8UC1);
491         cpu_result.setTo(0);
492
493         for(vector<Rect>::const_iterator r = ob1.begin(); r != ob1.end(); r++)
494         {      
495             cv::Mat cpu_result_roi(cpu_result, *r);
496             cpu_result_roi.setTo(1);
497             cpu_result.copyTo(cpu_result);
498         }
499         int cpu_area = cv::countNonZero(cpu_result > 0);
500
501         cv::Mat gpu_result(sz, CV_8UC1);
502         gpu_result.setTo(0);
503         for(vector<Rect>::const_iterator r2 = ob2.begin(); r2 != ob2.end(); r2++)
504         {
505             cv::Mat gpu_result_roi(gpu_result, *r2);
506             gpu_result_roi.setTo(1);
507             gpu_result.copyTo(gpu_result);
508         }
509
510         cv::Mat result_;
511         multiply(cpu_result, gpu_result, result_);
512         int result = cv::countNonZero(result_ > 0);
513
514         final_test_result = 1.0 - (double)result/(double)cpu_area;
515     }
516     return final_test_result;
517
518 }
519