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 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 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 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 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 331 inline void App::hogWorkBegin() 332 { 333 hog_work_begin = getTickCount(); 334 } 335 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 343 inline string App::hogWorkFps() const 344 { 345 stringstream ss; 346 ss << hog_work_fps; 347 return ss.str(); 348 } 349 350 inline void App::workBegin() 351 { 352 work_begin = getTickCount(); 353 } 354 355 inline void App::workEnd() 356 { 357 int64 delta = getTickCount() - work_begin; 358 double freq = getTickFrequency(); 359 work_fps = freq / delta; 360 } 361 362 inline string App::workFps() const 363 { 364 stringstream ss; 365 ss << work_fps; 366 return ss.str(); 367 } 368