OpenCV DNN backend Inference Engine (OpenVINO: Python, C++)

## Introduction

OpenCV DNN の OpenVINO backend について、Python と C++ を並べたサンプルが見当たらないので置いてみた。同一と言っても過言ではない。。。よくできている。

## OpenVINO Pre-Trained Model

- [face-detection-adas-0001](https://docs.openvinotoolkit.org/2018_R5/_docs_Transportation_object_detection_face_pruned_mobilenet_reduced_ssd_shared_weights_caffe_desc_face_detection_adas_0001.html)
- https://storage.openvinotoolkit.org/repositories/open_model_zoo/2021.2/models_bin/1/face-detection-adas-0001/FP16/

入力： [1x3x384x672] - [BxCxHxW]
出力： [1, 1, N, 7] - [image_id, label, conf, x_min, y_min, x_max, y_max]

## Python

```Python:facedetect.py
import argparse
import cv2 as cv

def facedetect(device, threshold):

    # load model
    net = cv.dnn.readNet('face-detection-adas-0001.xml',
                         'face-detection-adas-0001.bin')

    # input size of Neural Nwtwork
    inputsize = (672, 384)

    # output name
    outNames = cv.dnn_Net.getLayerNames(net)

    # select backend
    net.setPreferableBackend(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE)

    # select inference device
    if device == 'MYRIAD':
        net.setPreferableTarget(cv.dnn.DNN_TARGET_MYRIAD)
    elif device == 'CPU':
        net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)
    else:
        print("Unkown device: " + device)
        exit(0)

    # capture device
    cap = cv.VideoCapture(0)

    # set capcture image size
    cap.set(cv.CAP_PROP_FRAME_WIDTH, 800)
    cap.set(cv.CAP_PROP_FRAME_HEIGHT, 600)

    while cv.waitKey(16) != 27:

        _, frame = cap.read()

        # create a 4D blob from a frame.
        blob = cv.dnn.blobFromImage(frame, size=inputsize, ddepth=cv.CV_8U)

        # set the input to the network
        net.setInput(blob)

        # inference
        out = net.forward(outNames[0])

        # [1, 1, N, 7] -> [N, 7]
        for detection in out.reshape(-1, 7):
            confidence = float(detection[2])

            if confidence > threshold:
                xmin = int(detection[3] * frame.shape[1])
                ymin = int(detection[4] * frame.shape[0])
                xmax = int(detection[5] * frame.shape[1])
                ymax = int(detection[6] * frame.shape[0])
                cv.rectangle(frame, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2)

        cv.imshow('facedetect', frame)


def main():
    parser = argparse.ArgumentParser(description='Facedetect with OpenCV DNN API (OpenVINO)')
    parser.add_argument('--device', '-d', default="CPU")
    parser.add_argument('--threshold', '-t', type=float, default=0.9)
    args = parser.parse_args()

    facedetect(args.device, args.threshold)

if __name__ == '__main__':
    main()

```

## C++

```cpp:facedetect.cpp
#include <iostream>
#include <stdexcept>

#include <opencv2/opencv.hpp>
#include <gflags/gflags.h>

void facedetect(std::string device, double threshold)
{
  // load model
  cv::dnn::Net net = cv::dnn::readNet("face-detection-adas-0001.xml",
                                      "face-detection-adas-0001.bin");

  // input size of Neural Network
  cv::Size inputsize(672, 384);

  // output name
  std::vector<std::string> outNames = net.getUnconnectedOutLayersNames();

  // select backend
  net.setPreferableBackend(cv::dnn::DNN_BACKEND_INFERENCE_ENGINE);

  // select inference device
  if (device == "MYRIAD") {
    net.setPreferableTarget(cv::dnn::DNN_TARGET_MYRIAD);
  } else if (device == "CPU") {
    net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
  } else {
    throw std::runtime_error("Unknown device: " + device);
  }

  // capture device
  cv::VideoCapture cap;
  cap.open(0);

  // set capture image size
  cap.set(cv::CAP_PROP_FRAME_WIDTH, 800);
  cap.set(cv::CAP_PROP_FRAME_HEIGHT, 600);

  cv::Mat frame;

  while (cv::waitKey(16) != 27) {

    // capture 1 frame
    cap >> frame;

    // create a 4D blob from a frame.
    cv::Mat blob;
    cv::dnn::blobFromImage(frame, blob, 1.0, inputsize, cv::Scalar(), false, false);

    // set the input to the network
    net.setInput(blob);

    // inference
    cv::Mat out = net.forward(outNames[0]);

    // [1, 1, N, 7] -> [N * 7]
    float* data = (float *)out.data; // .reshape(-1)

    for (int i = 0; i < out.total(); i += 7) {
      float confidence = data[i + 2];

      if (confidence > threshold) {
        int x_min = data[i + 3] * frame.cols;
        int y_min = data[i + 4] * frame.rows;
        int x_max = data[i + 5] * frame.cols;
        int y_max = data[i + 6] * frame.rows;
        cv::rectangle(frame, cv::Point(x_min, y_min), cv::Point(x_max, y_max), cv::Scalar(0, 255, 0), 2);
      }
    }
    cv::imshow("facedetect", frame);
  }
}

DEFINE_string(d, "CPU", "device: CPU or MYRIAD");
DEFINE_double(t, 0.9, "threshold");

int main(int argc, char *argv[])
{
  try {

    gflags::ParseCommandLineFlags(&argc, &argv, true);

    facedetect(FLAGS_d, FLAGS_t);

    return EXIT_SUCCESS;
  }

  // -*- error -*- //

  catch (std::bad_alloc &e) {
    std::cerr << "BAD ALLOC Exception : " << e.what() << std::endl;
    return EXIT_FAILURE;
  }

  catch (const std::exception& e) {
    std::cerr << "Error: "  << e.what() << std::endl;
    return EXIT_FAILURE;
  }

  catch (...) {
    std::cerr << "unknown exception" << std::endl;
    return EXIT_FAILURE;
  }
}
```
おんなじじゃん！

## Reference
- [cv::dnn::Net Class Reference](https://docs.opencv.org/3.4/db/d30/classcv_1_1dnn_1_1Net.html)
- source and CMakeLists.txt: github [Facedetect_OpenCV-OpenVINO](https://github.com/kose/Facedetect_OpenCV-OpenVINO)

## Note
非同期の forwardAsync() で複数出力対応してくんないかなあ。