1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 #include <sstream>
5 #include <iomanip>
6 #include <stdexcept>
7 #include <opencv2/core/ocl.hpp>
8 #include <opencv2/core/utility.hpp>
9 #include "opencv2/imgcodecs.hpp"
10 #include <opencv2/video.hpp>
11 #include <opencv2/videoio.hpp>
12 #include <opencv2/highgui.hpp>
13 #include <opencv2/objdetect.hpp>
14 #include <opencv2/imgproc.hpp>
15
16 using namespace std;
17 using namespace cv;
18
19 class App
20 {
21 public:
22 App(CommandLineParser& cmd);
23 void run();
24 void handleKey(char key);
25 void hogWorkBegin();
26 void hogWorkEnd();
27 string hogWorkFps() const;
28 void workBegin();
29 void workEnd();
30 string workFps() const;
31 string message() const;
32
33
34 // This function test if gpu_rst matches cpu_rst.
35 // If the two vectors are not equal, it will return the difference in vector size
36 // Else if will return
37 // (total diff of each cpu and gpu rects covered pixels)/(total cpu rects covered pixels)
38 double checkRectSimilarity(Size sz,
39 std::vector<Rect>& cpu_rst,
40 std::vector<Rect>& gpu_rst);
41 private:
42 App operator=(App&);
43
44 //Args args;
45 bool running;
46 bool make_gray;
47 double scale;
48 double resize_scale;
49 int win_width;
50 int win_stride_width, win_stride_height;
51 int gr_threshold;
52 int nlevels;
53 double hit_threshold;
54 bool gamma_corr;
55
56 int64 hog_work_begin;
57 double hog_work_fps;
58 int64 work_begin;
59 double work_fps;
60
61 string img_source;
62 string vdo_source;
63 string output;
64 int camera_id;
65 bool write_once;
66 };
67
main(int argc,char ** argv)68 int main(int argc, char** argv)
69 {
70 const char* keys =
71 "{ h help | false | print help message }"
72 "{ i input | | specify input image}"
73 "{ c camera | -1 | enable camera capturing }"
74 "{ v video | ../data/768x576.avi | use video as input }"
75 "{ g gray | false | convert image to gray one or not}"
76 "{ s scale | 1.0 | resize the image before detect}"
77 "{ o output | | specify output path when input is images}";
78 CommandLineParser cmd(argc, argv, keys);
79 if (cmd.has("help"))
80 {
81 cout << "Usage : hog [options]" << endl;
82 cout << "Available options:" << endl;
83 cmd.printMessage();
84 return EXIT_SUCCESS;
85 }
86
87 App app(cmd);
88 try
89 {
90 app.run();
91 }
92 catch (const Exception& e)
93 {
94 return cout << "error: " << e.what() << endl, 1;
95 }
96 catch (const exception& e)
97 {
98 return cout << "error: " << e.what() << endl, 1;
99 }
100 catch(...)
101 {
102 return cout << "unknown exception" << endl, 1;
103 }
104 return EXIT_SUCCESS;
105 }
106
App(CommandLineParser & cmd)107 App::App(CommandLineParser& cmd)
108 {
109 cout << "\nControls:\n"
110 << "\tESC - exit\n"
111 << "\tm - change mode GPU <-> CPU\n"
112 << "\tg - convert image to gray or not\n"
113 << "\to - save output image once, or switch on/off video save\n"
114 << "\t1/q - increase/decrease HOG scale\n"
115 << "\t2/w - increase/decrease levels count\n"
116 << "\t3/e - increase/decrease HOG group threshold\n"
117 << "\t4/r - increase/decrease hit threshold\n"
118 << endl;
119
120 make_gray = cmd.has("gray");
121 resize_scale = cmd.get<double>("s");
122 vdo_source = cmd.get<string>("v");
123 img_source = cmd.get<string>("i");
124 output = cmd.get<string>("o");
125 camera_id = cmd.get<int>("c");
126
127 win_width = 48;
128 win_stride_width = 8;
129 win_stride_height = 8;
130 gr_threshold = 8;
131 nlevels = 13;
132 hit_threshold = 1.4;
133 scale = 1.05;
134 gamma_corr = true;
135 write_once = false;
136
137 cout << "Group threshold: " << gr_threshold << endl;
138 cout << "Levels number: " << nlevels << endl;
139 cout << "Win width: " << win_width << endl;
140 cout << "Win stride: (" << win_stride_width << ", " << win_stride_height << ")\n";
141 cout << "Hit threshold: " << hit_threshold << endl;
142 cout << "Gamma correction: " << gamma_corr << endl;
143 cout << endl;
144 }
145
run()146 void App::run()
147 {
148 running = true;
149 VideoWriter video_writer;
150
151 Size win_size(win_width, win_width * 2);
152 Size win_stride(win_stride_width, win_stride_height);
153
154 // Create HOG descriptors and detectors here
155
156 HOGDescriptor hog(win_size, Size(16, 16), Size(8, 8), Size(8, 8), 9, 1, -1,
157 HOGDescriptor::L2Hys, 0.2, gamma_corr, cv::HOGDescriptor::DEFAULT_NLEVELS);
158 hog.setSVMDetector( HOGDescriptor::getDaimlerPeopleDetector() );
159
160 while (running)
161 {
162 VideoCapture vc;
163 UMat frame;
164
165 if (vdo_source!="")
166 {
167 vc.open(vdo_source.c_str());
168 if (!vc.isOpened())
169 throw runtime_error(string("can't open video file: " + vdo_source));
170 vc >> frame;
171 }
172 else if (camera_id != -1)
173 {
174 vc.open(camera_id);
175 if (!vc.isOpened())
176 {
177 stringstream msg;
178 msg << "can't open camera: " << camera_id;
179 throw runtime_error(msg.str());
180 }
181 vc >> frame;
182 }
183 else
184 {
185 imread(img_source).copyTo(frame);
186 if (frame.empty())
187 throw runtime_error(string("can't open image file: " + img_source));
188 }
189
190 UMat img_aux, img;
191 Mat img_to_show;
192
193 // Iterate over all frames
194 while (running && !frame.empty())
195 {
196 workBegin();
197
198 // Change format of the image
199 if (make_gray) cvtColor(frame, img_aux, COLOR_BGR2GRAY );
200 else frame.copyTo(img_aux);
201
202 // Resize image
203 if (abs(scale-1.0)>0.001)
204 {
205 Size sz((int)((double)img_aux.cols/resize_scale), (int)((double)img_aux.rows/resize_scale));
206 resize(img_aux, img, sz);
207 }
208 else img = img_aux;
209 img.copyTo(img_to_show);
210 hog.nlevels = nlevels;
211 vector<Rect> found;
212
213 // Perform HOG classification
214 hogWorkBegin();
215
216 hog.detectMultiScale(img, found, hit_threshold, win_stride,
217 Size(0, 0), scale, gr_threshold);
218 hogWorkEnd();
219
220
221 // Draw positive classified windows
222 for (size_t i = 0; i < found.size(); i++)
223 {
224 Rect r = found[i];
225 rectangle(img_to_show, r.tl(), r.br(), Scalar(0, 255, 0), 3);
226 }
227
228 putText(img_to_show, ocl::useOpenCL() ? "Mode: OpenCL" : "Mode: CPU", Point(5, 25), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
229 putText(img_to_show, "FPS (HOG only): " + hogWorkFps(), Point(5, 65), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
230 putText(img_to_show, "FPS (total): " + workFps(), Point(5, 105), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
231 imshow("opencv_hog", img_to_show);
232 if (vdo_source!="" || camera_id!=-1) vc >> frame;
233
234 workEnd();
235
236 if (output!="" && write_once)
237 {
238 if (img_source!="") // wirte image
239 {
240 write_once = false;
241 imwrite(output, img_to_show);
242 }
243 else //write video
244 {
245 if (!video_writer.isOpened())
246 {
247 video_writer.open(output, VideoWriter::fourcc('x','v','i','d'), 24,
248 img_to_show.size(), true);
249 if (!video_writer.isOpened())
250 throw std::runtime_error("can't create video writer");
251 }
252
253 if (make_gray) cvtColor(img_to_show, img, COLOR_GRAY2BGR);
254 else cvtColor(img_to_show, img, COLOR_BGRA2BGR);
255
256 video_writer << img.getMat(ACCESS_READ);
257 }
258 }
259
260 handleKey((char)waitKey(3));
261 }
262 }
263 }
264
handleKey(char key)265 void App::handleKey(char key)
266 {
267 switch (key)
268 {
269 case 27:
270 running = false;
271 break;
272 case 'm':
273 case 'M':
274 ocl::setUseOpenCL(!cv::ocl::useOpenCL());
275 cout << "Switched to " << (ocl::useOpenCL() ? "OpenCL enabled" : "CPU") << " mode\n";
276 break;
277 case 'g':
278 case 'G':
279 make_gray = !make_gray;
280 cout << "Convert image to gray: " << (make_gray ? "YES" : "NO") << endl;
281 break;
282 case '1':
283 scale *= 1.05;
284 cout << "Scale: " << scale << endl;
285 break;
286 case 'q':
287 case 'Q':
288 scale /= 1.05;
289 cout << "Scale: " << scale << endl;
290 break;
291 case '2':
292 nlevels++;
293 cout << "Levels number: " << nlevels << endl;
294 break;
295 case 'w':
296 case 'W':
297 nlevels = max(nlevels - 1, 1);
298 cout << "Levels number: " << nlevels << endl;
299 break;
300 case '3':
301 gr_threshold++;
302 cout << "Group threshold: " << gr_threshold << endl;
303 break;
304 case 'e':
305 case 'E':
306 gr_threshold = max(0, gr_threshold - 1);
307 cout << "Group threshold: " << gr_threshold << endl;
308 break;
309 case '4':
310 hit_threshold+=0.25;
311 cout << "Hit threshold: " << hit_threshold << endl;
312 break;
313 case 'r':
314 case 'R':
315 hit_threshold = max(0.0, hit_threshold - 0.25);
316 cout << "Hit threshold: " << hit_threshold << endl;
317 break;
318 case 'c':
319 case 'C':
320 gamma_corr = !gamma_corr;
321 cout << "Gamma correction: " << gamma_corr << endl;
322 break;
323 case 'o':
324 case 'O':
325 write_once = !write_once;
326 break;
327 }
328 }
329
330
hogWorkBegin()331 inline void App::hogWorkBegin()
332 {
333 hog_work_begin = getTickCount();
334 }
335
hogWorkEnd()336 inline void App::hogWorkEnd()
337 {
338 int64 delta = getTickCount() - hog_work_begin;
339 double freq = getTickFrequency();
340 hog_work_fps = freq / delta;
341 }
342
hogWorkFps() const343 inline string App::hogWorkFps() const
344 {
345 stringstream ss;
346 ss << hog_work_fps;
347 return ss.str();
348 }
349
workBegin()350 inline void App::workBegin()
351 {
352 work_begin = getTickCount();
353 }
354
workEnd()355 inline void App::workEnd()
356 {
357 int64 delta = getTickCount() - work_begin;
358 double freq = getTickFrequency();
359 work_fps = freq / delta;
360 }
361
workFps() const362 inline string App::workFps() const
363 {
364 stringstream ss;
365 ss << work_fps;
366 return ss.str();
367 }
368