Merge pull request #1263 from abidrahmank:pyCLAHE_24
[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 class App
14 {
15 public:
16     App(CommandLineParser& cmd);
17     void run();
18     void handleKey(char key);
19     void hogWorkBegin();
20     void hogWorkEnd();
21     string hogWorkFps() const;
22     void workBegin();
23     void workEnd();
24     string workFps() const;
25     string message() const;
26
27
28 // This function test if gpu_rst matches cpu_rst.
29 // If the two vectors are not equal, it will return the difference in vector size
30 // Else if will return
31 // (total diff of each cpu and gpu rects covered pixels)/(total cpu rects covered pixels)
32     double checkRectSimilarity(Size sz,
33                                std::vector<Rect>& cpu_rst,
34                                std::vector<Rect>& gpu_rst);
35 private:
36     App operator=(App&);
37
38     //Args args;
39     bool running;
40     bool use_gpu;
41     bool make_gray;
42     double scale;
43     double resize_scale;
44     int win_width;
45     int win_stride_width, win_stride_height;
46     int gr_threshold;
47     int nlevels;
48     double hit_threshold;
49     bool gamma_corr;
50
51     int64 hog_work_begin;
52     double hog_work_fps;
53     int64 work_begin;
54     double work_fps;
55
56     string img_source;
57     string vdo_source;
58     string output;
59     int camera_id;
60     bool write_once;
61 };
62
63 int main(int argc, char** argv)
64 {
65     const char* keys =
66         "{ h |  help    | false          | print help message }"
67         "{ i |  input   |                | specify input image}"
68         "{ c | camera   | -1             | enable camera capturing }"
69         "{ v | video    |                | use video as input }"
70         "{ g |  gray    | false          | convert image to gray one or not}"
71         "{ s |  scale   | 1.0            | resize the image before detect}"
72         "{ l |larger_win| false          | use 64x128 window}"
73         "{ o |  output  |                | specify output path when input is images}";
74     CommandLineParser cmd(argc, argv, keys);
75     App app(cmd);
76     try
77     {
78         app.run();
79     }
80     catch (const Exception& e)
81     {
82         return cout << "error: "  << e.what() << endl, 1;
83     }
84     catch (const exception& e)
85     {
86         return cout << "error: "  << e.what() << endl, 1;
87     }
88     catch(...)
89     {
90         return cout << "unknown exception" << endl, 1;
91     }
92     return 0;
93 }
94
95 App::App(CommandLineParser& cmd)
96 {
97     cout << "\nControls:\n"
98          << "\tESC - exit\n"
99          << "\tm - change mode GPU <-> CPU\n"
100          << "\tg - convert image to gray or not\n"
101          << "\to - save output image once, or switch on/off video save\n"
102          << "\t1/q - increase/decrease HOG scale\n"
103          << "\t2/w - increase/decrease levels count\n"
104          << "\t3/e - increase/decrease HOG group threshold\n"
105          << "\t4/r - increase/decrease hit threshold\n"
106          << endl;
107
108
109     use_gpu = true;
110     make_gray = cmd.get<bool>("g");
111     resize_scale = cmd.get<double>("s");
112     win_width = cmd.get<bool>("l") == true ? 64 : 48;
113     vdo_source = cmd.get<string>("v");
114     img_source = cmd.get<string>("i");
115     output = cmd.get<string>("o");
116     camera_id = cmd.get<int>("c");
117
118     win_stride_width = 8;
119     win_stride_height = 8;
120     gr_threshold = 8;
121     nlevels = 13;
122     hit_threshold = win_width == 48 ? 1.4 : 0.;
123     scale = 1.05;
124     gamma_corr = true;
125     write_once = false;
126
127     cout << "Group threshold: " << gr_threshold << endl;
128     cout << "Levels number: " << nlevels << endl;
129     cout << "Win width: " << win_width << endl;
130     cout << "Win stride: (" << win_stride_width << ", " << win_stride_height << ")\n";
131     cout << "Hit threshold: " << hit_threshold << endl;
132     cout << "Gamma correction: " << gamma_corr << endl;
133     cout << endl;
134 }
135
136 void App::run()
137 {
138     vector<ocl::Info> oclinfo;
139     ocl::getDevice(oclinfo);
140     running = true;
141     VideoWriter video_writer;
142
143     Size win_size(win_width, win_width * 2);
144     Size win_stride(win_stride_width, win_stride_height);
145
146     // Create HOG descriptors and detectors here
147     vector<float> detector;
148     if (win_size == Size(64, 128))
149         detector = ocl::HOGDescriptor::getPeopleDetector64x128();
150     else
151         detector = ocl::HOGDescriptor::getPeopleDetector48x96();
152
153
154     ocl::HOGDescriptor gpu_hog(win_size, Size(16, 16), Size(8, 8), Size(8, 8), 9,
155                                ocl::HOGDescriptor::DEFAULT_WIN_SIGMA, 0.2, gamma_corr,
156                                ocl::HOGDescriptor::DEFAULT_NLEVELS);
157     HOGDescriptor cpu_hog(win_size, Size(16, 16), Size(8, 8), Size(8, 8), 9, 1, -1,
158                           HOGDescriptor::L2Hys, 0.2, gamma_corr, cv::HOGDescriptor::DEFAULT_NLEVELS);
159     gpu_hog.setSVMDetector(detector);
160     cpu_hog.setSVMDetector(detector);
161
162     while (running)
163     {
164         VideoCapture vc;
165         Mat frame;
166
167         if (vdo_source!="")
168         {
169             vc.open(vdo_source.c_str());
170             if (!vc.isOpened())
171                 throw runtime_error(string("can't open video file: " + vdo_source));
172             vc >> frame;
173         }
174         else if (camera_id != -1)
175         {
176             vc.open(camera_id);
177             if (!vc.isOpened())
178             {
179                 stringstream msg;
180                 msg << "can't open camera: " << camera_id;
181                 throw runtime_error(msg.str());
182             }
183             vc >> frame;
184         }
185         else
186         {
187             frame = imread(img_source);
188             if (frame.empty())
189                 throw runtime_error(string("can't open image file: " + img_source));
190         }
191
192         Mat img_aux, img, img_to_show;
193         ocl::oclMat gpu_img;
194
195         // Iterate over all frames
196         bool verify = false;
197         while (running && !frame.empty())
198         {
199             workBegin();
200
201             // Change format of the image
202             if (make_gray) cvtColor(frame, img_aux, CV_BGR2GRAY);
203             else if (use_gpu) cvtColor(frame, img_aux, CV_BGR2BGRA);
204             else frame.copyTo(img_aux);
205
206             // Resize image
207             if (abs(scale-1.0)>0.001)
208             {
209                 Size sz((int)((double)img_aux.cols/resize_scale), (int)((double)img_aux.rows/resize_scale));
210                 resize(img_aux, img, sz);
211             }
212             else img = img_aux;
213             img_to_show = img;
214             gpu_hog.nlevels = nlevels;
215             cpu_hog.nlevels = nlevels;
216             vector<Rect> found;
217
218             // Perform HOG classification
219             hogWorkBegin();
220             if (use_gpu)
221             {
222                 gpu_img.upload(img);
223                 gpu_hog.detectMultiScale(gpu_img, found, hit_threshold, win_stride,
224                                          Size(0, 0), scale, gr_threshold);
225                 if (!verify)
226                 {
227                     // verify if GPU output same objects with CPU at 1st run
228                     verify = true;
229                     vector<Rect> ref_rst;
230                     cvtColor(img, img, CV_BGRA2BGR);
231                     cpu_hog.detectMultiScale(img, ref_rst, hit_threshold, win_stride,
232                                              Size(0, 0), scale, gr_threshold-2);
233                     double accuracy = checkRectSimilarity(img.size(), ref_rst, found);
234                     cout << "\naccuracy value: " << accuracy << endl;
235                 }
236             }
237             else cpu_hog.detectMultiScale(img, found, hit_threshold, win_stride,
238                                               Size(0, 0), scale, gr_threshold);
239             hogWorkEnd();
240
241
242             // Draw positive classified windows
243             for (size_t i = 0; i < found.size(); i++)
244             {
245                 Rect r = found[i];
246                 rectangle(img_to_show, r.tl(), r.br(), CV_RGB(0, 255, 0), 3);
247             }
248
249             if (use_gpu)
250                 putText(img_to_show, "Mode: GPU", Point(5, 25), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
251             else
252                 putText(img_to_show, "Mode: CPU", Point(5, 25), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
253             putText(img_to_show, "FPS (HOG only): " + hogWorkFps(), Point(5, 65), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
254             putText(img_to_show, "FPS (total): " + workFps(), Point(5, 105), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
255             imshow("opencv_gpu_hog", img_to_show);
256             if (vdo_source!="" || camera_id!=-1) vc >> frame;
257
258             workEnd();
259
260             if (output!="" && write_once)
261             {
262                 if (img_source!="")     // wirte image
263                 {
264                     write_once = false;
265                     imwrite(output, img_to_show);
266                 }
267                 else                    //write video
268                 {
269                     if (!video_writer.isOpened())
270                     {
271                         video_writer.open(output, CV_FOURCC('x','v','i','d'), 24,
272                                           img_to_show.size(), true);
273                         if (!video_writer.isOpened())
274                             throw std::runtime_error("can't create video writer");
275                     }
276
277                     if (make_gray) cvtColor(img_to_show, img, CV_GRAY2BGR);
278                     else cvtColor(img_to_show, img, CV_BGRA2BGR);
279
280                     video_writer << img;
281                 }
282             }
283
284             handleKey((char)waitKey(3));
285         }
286     }
287 }
288
289 void App::handleKey(char key)
290 {
291     switch (key)
292     {
293     case 27:
294         running = false;
295         break;
296     case 'm':
297     case 'M':
298         use_gpu = !use_gpu;
299         cout << "Switched to " << (use_gpu ? "CUDA" : "CPU") << " mode\n";
300         break;
301     case 'g':
302     case 'G':
303         make_gray = !make_gray;
304         cout << "Convert image to gray: " << (make_gray ? "YES" : "NO") << endl;
305         break;
306     case '1':
307         scale *= 1.05;
308         cout << "Scale: " << scale << endl;
309         break;
310     case 'q':
311     case 'Q':
312         scale /= 1.05;
313         cout << "Scale: " << scale << endl;
314         break;
315     case '2':
316         nlevels++;
317         cout << "Levels number: " << nlevels << endl;
318         break;
319     case 'w':
320     case 'W':
321         nlevels = max(nlevels - 1, 1);
322         cout << "Levels number: " << nlevels << endl;
323         break;
324     case '3':
325         gr_threshold++;
326         cout << "Group threshold: " << gr_threshold << endl;
327         break;
328     case 'e':
329     case 'E':
330         gr_threshold = max(0, gr_threshold - 1);
331         cout << "Group threshold: " << gr_threshold << endl;
332         break;
333     case '4':
334         hit_threshold+=0.25;
335         cout << "Hit threshold: " << hit_threshold << endl;
336         break;
337     case 'r':
338     case 'R':
339         hit_threshold = max(0.0, hit_threshold - 0.25);
340         cout << "Hit threshold: " << hit_threshold << endl;
341         break;
342     case 'c':
343     case 'C':
344         gamma_corr = !gamma_corr;
345         cout << "Gamma correction: " << gamma_corr << endl;
346         break;
347     case 'o':
348     case 'O':
349         write_once = !write_once;
350         break;
351     }
352 }
353
354
355 inline void App::hogWorkBegin()
356 {
357     hog_work_begin = getTickCount();
358 }
359
360 inline void App::hogWorkEnd()
361 {
362     int64 delta = getTickCount() - hog_work_begin;
363     double freq = getTickFrequency();
364     hog_work_fps = freq / delta;
365 }
366
367 inline string App::hogWorkFps() const
368 {
369     stringstream ss;
370     ss << hog_work_fps;
371     return ss.str();
372 }
373
374 inline void App::workBegin()
375 {
376     work_begin = getTickCount();
377 }
378
379 inline void App::workEnd()
380 {
381     int64 delta = getTickCount() - work_begin;
382     double freq = getTickFrequency();
383     work_fps = freq / delta;
384 }
385
386 inline string App::workFps() const
387 {
388     stringstream ss;
389     ss << work_fps;
390     return ss.str();
391 }
392
393
394 double App::checkRectSimilarity(Size sz,
395                                 std::vector<Rect>& ob1,
396                                 std::vector<Rect>& ob2)
397 {
398     double final_test_result = 0.0;
399     size_t sz1 = ob1.size();
400     size_t sz2 = ob2.size();
401
402     if(sz1 != sz2)
403     {
404         return sz1 > sz2 ? (double)(sz1 - sz2) : (double)(sz2 - sz1);
405     }
406     else
407     {
408         if(sz1==0 && sz2==0)
409             return 0;
410         cv::Mat cpu_result(sz, CV_8UC1);
411         cpu_result.setTo(0);
412
413
414         for(vector<Rect>::const_iterator r = ob1.begin(); r != ob1.end(); r++)
415         {
416             cv::Mat cpu_result_roi(cpu_result, *r);
417             cpu_result_roi.setTo(1);
418             cpu_result.copyTo(cpu_result);
419         }
420         int cpu_area = cv::countNonZero(cpu_result > 0);
421
422
423         cv::Mat gpu_result(sz, CV_8UC1);
424         gpu_result.setTo(0);
425         for(vector<Rect>::const_iterator r2 = ob2.begin(); r2 != ob2.end(); r2++)
426         {
427             cv::Mat gpu_result_roi(gpu_result, *r2);
428             gpu_result_roi.setTo(1);
429             gpu_result.copyTo(gpu_result);
430         }
431
432         cv::Mat result_;
433         multiply(cpu_result, gpu_result, result_);
434         int result = cv::countNonZero(result_ > 0);
435         if(cpu_area!=0 && result!=0)
436             final_test_result = 1.0 - (double)result/(double)cpu_area;
437         else if(cpu_area==0 && result!=0)
438             final_test_result = -1;
439     }
440     return final_test_result;
441 }
442