#include #include #include #include #include #include #include #include "opencv2/ocl.hpp" #include "opencv2/highgui.hpp" using namespace std; using namespace cv; bool help_showed = false; class Args { public: Args(); static Args read(int argc, char** argv); string src; bool src_is_video; bool src_is_camera; int camera_id; bool write_video; string dst_video; double dst_video_fps; bool make_gray; bool resize_src; int width, height; double scale; int nlevels; int gr_threshold; double hit_threshold; bool hit_threshold_auto; int win_width; int win_stride_width, win_stride_height; bool gamma_corr; }; class App { public: App(const Args& s); void run(); void handleKey(char key); void hogWorkBegin(); void hogWorkEnd(); string hogWorkFps() const; void workBegin(); void workEnd(); string workFps() const; string message() const; // This function test if gpu_rst matches cpu_rst. // If the two vectors are not equal, it will return the difference in vector size // Else if will return // (total diff of each cpu and gpu rects covered pixels)/(total cpu rects covered pixels) double checkRectSimilarity(Size sz, std::vector& cpu_rst, std::vector& gpu_rst); private: App operator=(App&); Args args; bool running; bool use_gpu; bool make_gray; double scale; int gr_threshold; int nlevels; double hit_threshold; bool gamma_corr; int64 hog_work_begin; double hog_work_fps; int64 work_begin; double work_fps; }; static void printHelp() { cout << "Histogram of Oriented Gradients descriptor and detector sample.\n" << "\nUsage: hog_gpu\n" << " (|--video |--camera ) # frames source\n" << " [--make_gray ] # convert image to gray one or not\n" << " [--resize_src ] # do resize of the source image or not\n" << " [--width ] # resized image width\n" << " [--height ] # resized image height\n" << " [--hit_threshold ] # classifying plane distance threshold (0.0 usually)\n" << " [--scale ] # HOG window scale factor\n" << " [--nlevels ] # max number of HOG window scales\n" << " [--win_width ] # width of the window (48 or 64)\n" << " [--win_stride_width ] # distance by OX axis between neighbour wins\n" << " [--win_stride_height ] # distance by OY axis between neighbour wins\n" << " [--gr_threshold ] # merging similar rects constant\n" << " [--gamma_correct ] # do gamma correction or not\n" << " [--write_video ] # write video or not\n" << " [--dst_video ] # output video path\n" << " [--dst_video_fps ] # output video fps\n"; help_showed = true; } int main(int argc, char** argv) { try { if (argc < 2) printHelp(); Args args = Args::read(argc, argv); if (help_showed) return -1; App app(args); app.run(); } catch (const Exception& e) { return cout << "error: " << e.what() << endl, 1; } catch (const exception& e) { return cout << "error: " << e.what() << endl, 1; } catch(...) { return cout << "unknown exception" << endl, 1; } return 0; } Args::Args() { src_is_video = false; src_is_camera = false; camera_id = 0; write_video = false; dst_video_fps = 24.; make_gray = false; resize_src = false; width = 640; height = 480; scale = 1.05; nlevels = 13; gr_threshold = 8; hit_threshold = 1.4; hit_threshold_auto = true; win_width = 48; win_stride_width = 8; win_stride_height = 8; gamma_corr = true; } Args Args::read(int argc, char** argv) { Args args; for (int i = 1; i < argc; i++) { if (string(argv[i]) == "--make_gray") args.make_gray = (string(argv[++i]) == "true"); else if (string(argv[i]) == "--resize_src") args.resize_src = (string(argv[++i]) == "true"); else if (string(argv[i]) == "--width") args.width = atoi(argv[++i]); else if (string(argv[i]) == "--height") args.height = atoi(argv[++i]); else if (string(argv[i]) == "--hit_threshold") { args.hit_threshold = atof(argv[++i]); args.hit_threshold_auto = false; } else if (string(argv[i]) == "--scale") args.scale = atof(argv[++i]); else if (string(argv[i]) == "--nlevels") args.nlevels = atoi(argv[++i]); else if (string(argv[i]) == "--win_width") args.win_width = atoi(argv[++i]); else if (string(argv[i]) == "--win_stride_width") args.win_stride_width = atoi(argv[++i]); else if (string(argv[i]) == "--win_stride_height") args.win_stride_height = atoi(argv[++i]); else if (string(argv[i]) == "--gr_threshold") args.gr_threshold = atoi(argv[++i]); else if (string(argv[i]) == "--gamma_correct") args.gamma_corr = (string(argv[++i]) == "true"); else if (string(argv[i]) == "--write_video") args.write_video = (string(argv[++i]) == "true"); else if (string(argv[i]) == "--dst_video") args.dst_video = argv[++i]; else if (string(argv[i]) == "--dst_video_fps") args.dst_video_fps = atof(argv[++i]); else if (string(argv[i]) == "--help") printHelp(); else if (string(argv[i]) == "--video") { args.src = argv[++i]; args.src_is_video = true; } else if (string(argv[i]) == "--camera") { args.camera_id = atoi(argv[++i]); args.src_is_camera = true; } else if (args.src.empty()) args.src = argv[i]; else throw runtime_error((string("unknown key: ") + argv[i])); } return args; } App::App(const Args& s) { args = s; cout << "\nControls:\n" << "\tESC - exit\n" << "\tm - change mode GPU <-> CPU\n" << "\tg - convert image to gray or not\n" << "\t1/q - increase/decrease HOG scale\n" << "\t2/w - increase/decrease levels count\n" << "\t3/e - increase/decrease HOG group threshold\n" << "\t4/r - increase/decrease hit threshold\n" << endl; use_gpu = true; make_gray = args.make_gray; scale = args.scale; gr_threshold = args.gr_threshold; nlevels = args.nlevels; if (args.hit_threshold_auto) args.hit_threshold = args.win_width == 48 ? 1.4 : 0.; hit_threshold = args.hit_threshold; gamma_corr = args.gamma_corr; if (args.win_width != 64 && args.win_width != 48) args.win_width = 64; cout << "Scale: " << scale << endl; if (args.resize_src) cout << "Resized source: (" << args.width << ", " << args.height << ")\n"; cout << "Group threshold: " << gr_threshold << endl; cout << "Levels number: " << nlevels << endl; cout << "Win width: " << args.win_width << endl; cout << "Win stride: (" << args.win_stride_width << ", " << args.win_stride_height << ")\n"; cout << "Hit threshold: " << hit_threshold << endl; cout << "Gamma correction: " << gamma_corr << endl; cout << endl; } void App::run() { std::vector oclinfo; ocl::getDevice(oclinfo); running = true; cv::VideoWriter video_writer; Size win_size(args.win_width, args.win_width * 2); //(64, 128) or (48, 96) Size win_stride(args.win_stride_width, args.win_stride_height); // Create HOG descriptors and detectors here vector detector; if (win_size == Size(64, 128)) detector = cv::ocl::HOGDescriptor::getPeopleDetector64x128(); else detector = cv::ocl::HOGDescriptor::getPeopleDetector48x96(); cv::ocl::HOGDescriptor gpu_hog(win_size, Size(16, 16), Size(8, 8), Size(8, 8), 9, cv::ocl::HOGDescriptor::DEFAULT_WIN_SIGMA, 0.2, gamma_corr, cv::ocl::HOGDescriptor::DEFAULT_NLEVELS); cv::HOGDescriptor cpu_hog(win_size, Size(16, 16), Size(8, 8), Size(8, 8), 9, 1, -1, HOGDescriptor::L2Hys, 0.2, gamma_corr, cv::HOGDescriptor::DEFAULT_NLEVELS); gpu_hog.setSVMDetector(detector); cpu_hog.setSVMDetector(detector); while (running) { VideoCapture vc; Mat frame; if (args.src_is_video) { vc.open(args.src.c_str()); if (!vc.isOpened()) throw runtime_error(string("can't open video file: " + args.src)); vc >> frame; } else if (args.src_is_camera) { vc.open(args.camera_id); if (!vc.isOpened()) { stringstream msg; msg << "can't open camera: " << args.camera_id; throw runtime_error(msg.str()); } vc >> frame; } else { frame = imread(args.src); if (frame.empty()) throw runtime_error(string("can't open image file: " + args.src)); } Mat img_aux, img, img_to_show; ocl::oclMat gpu_img; // Iterate over all frames bool verify = false; while (running && !frame.empty()) { workBegin(); // Change format of the image if (make_gray) cvtColor(frame, img_aux, COLOR_BGR2GRAY); else if (use_gpu) cvtColor(frame, img_aux, COLOR_BGR2BGRA); else frame.copyTo(img_aux); // Resize image if (args.resize_src) resize(img_aux, img, Size(args.width, args.height)); else img = img_aux; img_to_show = img; gpu_hog.nlevels = nlevels; cpu_hog.nlevels = nlevels; vector found; // Perform HOG classification hogWorkBegin(); if (use_gpu) { gpu_img.upload(img); gpu_hog.detectMultiScale(gpu_img, found, hit_threshold, win_stride, Size(0, 0), scale, gr_threshold); if (!verify) { // verify if GPU output same objects with CPU at 1st run verify = true; vector ref_rst; cvtColor(img, img, COLOR_BGRA2BGR); cpu_hog.detectMultiScale(img, ref_rst, hit_threshold, win_stride, Size(0, 0), scale, gr_threshold-2); double accuracy = checkRectSimilarity(img.size(), ref_rst, found); cout << "\naccuracy value: " << accuracy << endl; } } else cpu_hog.detectMultiScale(img, found, hit_threshold, win_stride, Size(0, 0), scale, gr_threshold); hogWorkEnd(); // Draw positive classified windows for (size_t i = 0; i < found.size(); i++) { Rect r = found[i]; rectangle(img_to_show, r.tl(), r.br(), Scalar(0, 255, 0), 3); } if (use_gpu) putText(img_to_show, "Mode: GPU", Point(5, 25), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2); else putText(img_to_show, "Mode: CPU", Point(5, 25), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2); putText(img_to_show, "FPS (HOG only): " + hogWorkFps(), Point(5, 65), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2); putText(img_to_show, "FPS (total): " + workFps(), Point(5, 105), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2); imshow("opencv_gpu_hog", img_to_show); if (args.src_is_video || args.src_is_camera) vc >> frame; workEnd(); if (args.write_video) { if (!video_writer.isOpened()) { video_writer.open(args.dst_video, VideoWriter::fourcc('x','v','i','d'), args.dst_video_fps, img_to_show.size(), true); if (!video_writer.isOpened()) throw std::runtime_error("can't create video writer"); } if (make_gray) cvtColor(img_to_show, img, COLOR_GRAY2BGR); else cvtColor(img_to_show, img, COLOR_BGRA2BGR); video_writer << img; } handleKey((char)waitKey(3)); } } } void App::handleKey(char key) { switch (key) { case 27: running = false; break; case 'm': case 'M': use_gpu = !use_gpu; cout << "Switched to " << (use_gpu ? "CUDA" : "CPU") << " mode\n"; break; case 'g': case 'G': make_gray = !make_gray; cout << "Convert image to gray: " << (make_gray ? "YES" : "NO") << endl; break; case '1': scale *= 1.05; cout << "Scale: " << scale << endl; break; case 'q': case 'Q': scale /= 1.05; cout << "Scale: " << scale << endl; break; case '2': nlevels++; cout << "Levels number: " << nlevels << endl; break; case 'w': case 'W': nlevels = max(nlevels - 1, 1); cout << "Levels number: " << nlevels << endl; break; case '3': gr_threshold++; cout << "Group threshold: " << gr_threshold << endl; break; case 'e': case 'E': gr_threshold = max(0, gr_threshold - 1); cout << "Group threshold: " << gr_threshold << endl; break; case '4': hit_threshold+=0.25; cout << "Hit threshold: " << hit_threshold << endl; break; case 'r': case 'R': hit_threshold = max(0.0, hit_threshold - 0.25); cout << "Hit threshold: " << hit_threshold << endl; break; case 'c': case 'C': gamma_corr = !gamma_corr; cout << "Gamma correction: " << gamma_corr << endl; break; } } inline void App::hogWorkBegin() { hog_work_begin = getTickCount(); } inline void App::hogWorkEnd() { int64 delta = getTickCount() - hog_work_begin; double freq = getTickFrequency(); hog_work_fps = freq / delta; } inline string App::hogWorkFps() const { stringstream ss; ss << hog_work_fps; return ss.str(); } inline void App::workBegin() { work_begin = getTickCount(); } inline void App::workEnd() { int64 delta = getTickCount() - work_begin; double freq = getTickFrequency(); work_fps = freq / delta; } inline string App::workFps() const { stringstream ss; ss << work_fps; return ss.str(); } double App::checkRectSimilarity(Size sz, std::vector& ob1, std::vector& ob2) { double final_test_result = 0.0; size_t sz1 = ob1.size(); size_t sz2 = ob2.size(); if(sz1 != sz2) return sz1 > sz2 ? (double)(sz1 - sz2) : (double)(sz2 - sz1); else { cv::Mat cpu_result(sz, CV_8UC1); cpu_result.setTo(0); for(vector::const_iterator r = ob1.begin(); r != ob1.end(); r++) { cv::Mat cpu_result_roi(cpu_result, *r); cpu_result_roi.setTo(1); cpu_result.copyTo(cpu_result); } int cpu_area = cv::countNonZero(cpu_result > 0); cv::Mat gpu_result(sz, CV_8UC1); gpu_result.setTo(0); for(vector::const_iterator r2 = ob2.begin(); r2 != ob2.end(); r2++) { cv::Mat gpu_result_roi(gpu_result, *r2); gpu_result_roi.setTo(1); gpu_result.copyTo(gpu_result); } cv::Mat result_; multiply(cpu_result, gpu_result, result_); int result = cv::countNonZero(result_ > 0); final_test_result = 1.0 - (double)result/(double)cpu_area; } return final_test_result; }