OpenCV3で画像キャプチャして顔検出してモザイク処理してから表示×TBBパイプライン並列

せっかくのマルチコアマシンなのでIntel [Threading Building Blocks(TBB)](https://www.threadingbuildingblocks.org/)でマルチスレッド並列処理。

画像キャプチャ[逐次] → 顔検出＋モザイク処理[フレーム並列] → ウインドウ表示[逐次] のパイプラインを構築。

http://qiita.com/yohhoy/items/505815587d1fbb57cc8b × http://d.hatena.ne.jp/yohhoy/20140514/p1

```facemozp.cpp
#include <vector>
#include <string>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <tbb/tbb.h>


struct workset {
    cv::CascadeClassifier classifier;
};
std::vector<workset> ws;

const char * const file = "./haarcascade.xml";
cv::Size min_face;

//! initialize
void init(int w, int h, size_t nthread)
{
    std::cout << "frame=" << w << 'x' << h << " nthread=" << nthread << std::endl;

    ws.resize(nthread);
    for (auto& w : ws) {
        if (!w.classifier.load(file)) {
            std::cout << "fail to load: " << file << std::endl;
            std::exit(1);
        }
    }
    const double scale = 0.10;
    min_face = cv::Size(w * scale, h * scale);
    std::cout << "min_face=" << min_face << std::endl;
}

//! process image frame
cv::Mat process(cv::Mat& frame, workset& w)
{
    std::vector<cv::Rect> faces;
    w.classifier.detectMultiScale(frame, faces, 1.1, 3, 0, min_face);
    for (const auto& r : faces) {
        // mosaic filter
        const double f = 0.05;
        cv::Mat tmp;
        cv::resize(frame(r), tmp, {}, f, f);
        cv::resize(tmp, frame(r), r.size(), 0, 0, cv::INTER_NEAREST);
        // draw area border
        cv::rectangle(frame, r.tl(), r.br(), {0,0,0});
    }
    return frame;
}

int main()
{
    const char * const window = "Capture (Press ESC to exit)";
    const double scale = 0.75;
    const int delay = 32;   // [msec]
    const size_t nthread = tbb::task_scheduler_init::default_num_threads();

    cv::VideoCapture cap(0);
    if (!cap.isOpened()) {
        std::cerr << "fail to open cv::VideoCapture" << std::endl;
        return 2; 
    }

    const double width = cap.get(cv::CAP_PROP_FRAME_WIDTH) * scale;
    const double height = cap.get(cv::CAP_PROP_FRAME_HEIGHT) * scale;
    cap.set(cv::CAP_PROP_FRAME_WIDTH, width);
    cap.set(cv::CAP_PROP_FRAME_HEIGHT, height);
    init(width, height, nthread);

    cv::namedWindow(window);
    std::vector<cv::Mat> frames(nthread);
    size_t next_token = 0;
    auto filter_chain =
        tbb::make_filter<void, size_t>(
            tbb::filter::serial_in_order,
            [&](tbb::flow_control& fc) -> size_t {
                if (cv::waitKey(delay) == '\x1b') {
                    fc.stop();
                    return -1;
                }
                size_t token = (next_token++ % ws.size());
                cap >> frames[token];
                return token;
            }) &
        tbb::make_filter<size_t, size_t>(
            tbb::filter::parallel,
            [&](size_t token) -> size_t {
                process(frames[token], ws[token]);
                return token;
            }) &
        tbb::make_filter<size_t, void>(
            tbb::filter::serial_in_order,
            [&](size_t token) -> void {
                cv::imshow(window, frames[token]);
            });
    tbb::task_scheduler_init sched(nthread);
    tbb::parallel_pipeline(nthread, filter_chain);

    return 0;
}
```

```
$ brew install science/opencv3
...
$ brew install tbb
...
$ clang++ -std=c++14 -W -Wall -O2 -o facemozp facemozp.cpp -I/usr/local/opt/opencv3/include -I/usr/local/opt/tbb/include -L/usr/local/opt/opencv3/lib -L/usr/local/opt/tbb/include -lopencv_core -lopencv_highgui -lopencv_videoio -lopencv_objdetect -lopencv_imgproc -ltbb
$ cp /usr/local/opt/opencv3/share/OpenCV/haarcascades/haarcascade_frontalface_default.xml haarsascade.xml
$ ./facemozp
```