hog.cpp 13.9 KB
Newer Older
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
1 2 3 4 5
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <iomanip>
6
#include <stdexcept>
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
7 8 9 10 11 12
#include "opencv2/gpu/gpu.hpp"
#include "opencv2/highgui/highgui.hpp"

using namespace std;
using namespace cv;

13 14
bool help_showed = false;

15
class Args
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
16 17
{
public:
18 19
    Args();
    static Args read(int argc, char** argv);
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
20 21 22

    string src;
    bool src_is_video;
23 24 25
    bool src_is_camera;
    int camera_id;

26 27 28 29
    bool write_video;
    string dst_video;
    double dst_video_fps;

Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
30
    bool make_gray;
31

Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
32
    bool resize_src;
33
    int width, height;
34

Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
35 36 37
    double scale;
    int nlevels;
    int gr_threshold;
38

Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
39
    double hit_threshold;
40
    bool hit_threshold_auto;
41

Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
42
    int win_width;
43 44
    int win_stride_width, win_stride_height;

45
    bool gamma_corr;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
46 47 48 49 50
};


class App
{
51
public:
52 53
    App(const Args& s);
    void run();
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
54

55
    void handleKey(char key);
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
56

57 58 59
    void hogWorkBegin();
    void hogWorkEnd();
    string hogWorkFps() const;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
60

61 62 63
    void workBegin();
    void workEnd();
    string workFps() const;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
64

65
    string message() const;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
66 67 68 69

private:
    App operator=(App&);

70
    Args args;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
71 72 73 74 75 76 77 78
    bool running;

    bool use_gpu;
    bool make_gray;
    double scale;
    int gr_threshold;
    int nlevels;
    double hit_threshold;
79
    bool gamma_corr;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
80 81 82 83 84 85 86 87

    int64 hog_work_begin;
    double hog_work_fps;

    int64 work_begin;
    double work_fps;
};

88
static void printHelp()
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
{
    cout << "Histogram of Oriented Gradients descriptor and detector sample.\n"
         << "\nUsage: hog_gpu\n"
         << "  (<image>|--video <vide>|--camera <camera_id>) # frames source\n"
         << "  [--make_gray <true/false>] # convert image to gray one or not\n"
         << "  [--resize_src <true/false>] # do resize of the source image or not\n"
         << "  [--width <int>] # resized image width\n"
         << "  [--height <int>] # resized image height\n"
         << "  [--hit_threshold <double>] # classifying plane distance threshold (0.0 usually)\n"
         << "  [--scale <double>] # HOG window scale factor\n"
         << "  [--nlevels <int>] # max number of HOG window scales\n"
         << "  [--win_width <int>] # width of the window (48 or 64)\n"
         << "  [--win_stride_width <int>] # distance by OX axis between neighbour wins\n"
         << "  [--win_stride_height <int>] # distance by OY axis between neighbour wins\n"
         << "  [--gr_threshold <int>] # merging similar rects constant\n"
         << "  [--gamma_correct <int>] # do gamma correction or not\n"
         << "  [--write_video <bool>] # write video or not\n"
         << "  [--dst_video <path>] # output video path\n"
         << "  [--dst_video_fps <double>] # output video fps\n";
    help_showed = true;
}

Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
111 112 113 114 115
int main(int argc, char** argv)
{
    try
    {
        if (argc < 2)
116 117 118 119 120
            printHelp();
        Args args = Args::read(argc, argv);
        if (help_showed)
            return -1;
        App app(args);
121
        app.run();
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
122
    }
123 124 125
    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; }
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
126 127 128 129
    return 0;
}


130
Args::Args()
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
131 132
{
    src_is_video = false;
133 134 135
    src_is_camera = false;
    camera_id = 0;

136 137 138
    write_video = false;
    dst_video_fps = 24.;

Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
139
    make_gray = false;
140 141

    resize_src = false;
142 143
    width = 640;
    height = 480;
144

Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
145 146 147 148
    scale = 1.05;
    nlevels = 13;
    gr_threshold = 8;
    hit_threshold = 1.4;
149
    hit_threshold_auto = true;
150

Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
151
    win_width = 48;
152 153
    win_stride_width = 8;
    win_stride_height = 8;
154

155
    gamma_corr = true;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
156 157 158
}


159
Args Args::read(int argc, char** argv)
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
160
{
161
    Args args;
162
    for (int i = 1; i < argc; i++)
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
163
    {
164 165 166 167
        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]);
168 169 170 171
        else if (string(argv[i]) == "--hit_threshold")
        {
            args.hit_threshold = atof(argv[++i]);
            args.hit_threshold_auto = false;
172
        }
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
        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]));
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
188
    }
189
    return args;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
190 191 192
}


193
App::App(const Args& s)
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
194
{
195 196
    cv::gpu::printShortCudaDeviceInfo(cv::gpu::getDevice());

197
    args = s;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
198
    cout << "\nControls:\n"
199 200 201 202 203 204 205
         << "\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"
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
206 207 208
         << endl;

    use_gpu = true;
209 210 211 212
    make_gray = args.make_gray;
    scale = args.scale;
    gr_threshold = args.gr_threshold;
    nlevels = args.nlevels;
213 214 215

    if (args.hit_threshold_auto)
        args.hit_threshold = args.win_width == 48 ? 1.4 : 0.;
216
    hit_threshold = args.hit_threshold;
217

218
    gamma_corr = args.gamma_corr;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
219

220 221
    if (args.win_width != 64 && args.win_width != 48)
        args.win_width = 64;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
222

223
    cout << "Scale: " << scale << endl;
224
    if (args.resize_src)
225
        cout << "Resized source: (" << args.width << ", " << args.height << ")\n";
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
226 227
    cout << "Group threshold: " << gr_threshold << endl;
    cout << "Levels number: " << nlevels << endl;
228 229
    cout << "Win width: " << args.win_width << endl;
    cout << "Win stride: (" << args.win_stride_width << ", " << args.win_stride_height << ")\n";
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
230
    cout << "Hit threshold: " << hit_threshold << endl;
231
    cout << "Gamma correction: " << gamma_corr << endl;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
232 233 234
    cout << endl;
}

Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
235

236
void App::run()
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
237 238
{
    running = true;
239
    cv::VideoWriter video_writer;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
240

241 242
    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);
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
243

244
    // Create HOG descriptors and detectors here
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
245
    vector<float> detector;
246
    if (win_size == Size(64, 128))
247
        detector = cv::gpu::HOGDescriptor::getPeopleDetector64x128();
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
248
    else
249
        detector = cv::gpu::HOGDescriptor::getPeopleDetector48x96();
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
250

251 252
    cv::gpu::HOGDescriptor gpu_hog(win_size, Size(16, 16), Size(8, 8), Size(8, 8), 9,
                                   cv::gpu::HOGDescriptor::DEFAULT_WIN_SIGMA, 0.2, gamma_corr,
253
                                   cv::gpu::HOGDescriptor::DEFAULT_NLEVELS);
254
    cv::HOGDescriptor cpu_hog(win_size, Size(16, 16), Size(8, 8), Size(8, 8), 9, 1, -1,
255
                              HOGDescriptor::L2Hys, 0.2, gamma_corr, cv::HOGDescriptor::DEFAULT_NLEVELS);
256
    gpu_hog.setSVMDetector(detector);
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
257 258 259 260 261 262
    cpu_hog.setSVMDetector(detector);

    while (running)
    {
        VideoCapture vc;
        Mat frame;
263

264
        if (args.src_is_video)
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
265
        {
266
            vc.open(args.src.c_str());
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
267
            if (!vc.isOpened())
268 269 270 271 272 273 274
                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())
275 276 277 278 279
            {
                stringstream msg;
                msg << "can't open camera: " << args.camera_id;
                throw runtime_error(msg.str());
            }
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
280 281 282
            vc >> frame;
        }
        else
283
        {
284
            frame = imread(args.src);
285
            if (frame.empty())
286
                throw runtime_error(string("can't open image file: " + args.src));
287
        }
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
288 289 290 291 292 293 294

        Mat img_aux, img, img_to_show;
        gpu::GpuMat gpu_img;

        // Iterate over all frames
        while (running && !frame.empty())
        {
295
            workBegin();
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
296

297 298 299
            // Change format of the image
            if (make_gray) cvtColor(frame, img_aux, CV_BGR2GRAY);
            else if (use_gpu) cvtColor(frame, img_aux, CV_BGR2BGRA);
300
            else frame.copyTo(img_aux);
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
301 302

            // Resize image
303
            if (args.resize_src) resize(img_aux, img, Size(args.width, args.height));
304
            else img = img_aux;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
305 306 307 308 309
            img_to_show = img;

            gpu_hog.nlevels = nlevels;
            cpu_hog.nlevels = nlevels;

310 311
            vector<Rect> found;

Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
312
            // Perform HOG classification
313
            hogWorkBegin();
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
314 315
            if (use_gpu)
            {
316
                gpu_img.upload(img);
317
                gpu_hog.detectMultiScale(gpu_img, found, hit_threshold, win_stride,
318
                                         Size(0, 0), scale, gr_threshold);
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
319
            }
320
            else cpu_hog.detectMultiScale(img, found, hit_threshold, win_stride,
321 322
                                          Size(0, 0), scale, gr_threshold);
            hogWorkEnd();
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
323 324 325 326 327 328 329 330

            // 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(), CV_RGB(0, 255, 0), 3);
            }

331 332 333 334 335 336
            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);
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
337 338
            imshow("opencv_gpu_hog", img_to_show);

339
            if (args.src_is_video || args.src_is_camera) vc >> frame;
340

341
            workEnd();
342

343 344 345 346
            if (args.write_video)
            {
                if (!video_writer.isOpened())
                {
347
                    video_writer.open(args.dst_video, CV_FOURCC('x','v','i','d'), args.dst_video_fps,
348 349 350 351
                                      img_to_show.size(), true);
                    if (!video_writer.isOpened())
                        throw std::runtime_error("can't create video writer");
                }
352 353 354 355

                if (make_gray) cvtColor(img_to_show, img, CV_GRAY2BGR);
                else cvtColor(img_to_show, img, CV_BGRA2BGR);

356 357
                video_writer << img;
            }
358 359

            handleKey((char)waitKey(3));
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
360 361 362 363 364
        }
    }
}


365
void App::handleKey(char key)
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
{
    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;
418 419 420 421 422
    case 'c':
    case 'C':
        gamma_corr = !gamma_corr;
        cout << "Gamma correction: " << gamma_corr << endl;
        break;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
423 424 425 426
    }
}


427
inline void App::hogWorkBegin() { hog_work_begin = getTickCount(); }
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
428

429
inline void App::hogWorkEnd()
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
430 431 432 433 434 435
{
    int64 delta = getTickCount() - hog_work_begin;
    double freq = getTickFrequency();
    hog_work_fps = freq / delta;
}

436 437 438 439 440 441
inline string App::hogWorkFps() const
{
    stringstream ss;
    ss << hog_work_fps;
    return ss.str();
}
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
442 443


444
inline void App::workBegin() { work_begin = getTickCount(); }
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
445

446
inline void App::workEnd()
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
447 448 449 450 451 452
{
    int64 delta = getTickCount() - work_begin;
    double freq = getTickFrequency();
    work_fps = freq / delta;
}

453
inline string App::workFps() const
454
{
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
455
    stringstream ss;
456
    ss << work_fps;
Alexey Spizhevoy's avatar
Alexey Spizhevoy committed
457
    return ss.str();
458
}
459