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