OpenCV 4.3 (C++) で Hello World と顔検出

## 概要

- macOS に OpenCV 4.3 をインストール
- OpenCV 4.3 と C++ で基本的な処理を実行する
  - Hello World 画像生成
  - 画像ファイルをウィンドウ表示
  - カメラからの映像をリアルタイムにエッジ抽出
  - 画像から顔の位置を検出

## 環境

- macOS Catalina
- Clang 11

```
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.15.4
BuildVersion:	19E287
```

```
$ clang++ --version
Apple clang version 11.0.3 (clang-1103.0.32.59)
Target: x86_64-apple-darwin19.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
```

## インストール

### Homebrew でインストール

```
$ brew install opencv
```

```
$ brew info opencv
opencv: stable 4.3.0 (bottled)
Open source computer vision library
https://opencv.org/
/usr/local/Cellar/opencv/4.3.0 (771 files, 224.5MB) *
```

### ヘッダファイルの場所

```
$ ls -C /usr/local/include/opencv4/opencv2 | expand
alphamat.hpp            highgui.hpp             sfm.hpp
aruco                   img_hash                shape
aruco.hpp               img_hash.hpp            shape.hpp
bgsegm.hpp              imgcodecs               stereo
bioinspired             imgcodecs.hpp           stereo.hpp
bioinspired.hpp         imgproc                 stitching
calib3d                 imgproc.hpp             stitching.hpp
calib3d.hpp             intensity_transform.hpp structured_light
ccalib                  line_descriptor         structured_light.hpp
ccalib.hpp              line_descriptor.hpp     superres
core                    ml                      superres.hpp
core.hpp                ml.hpp                  surface_matching
core_detect.hpp         objdetect               surface_matching.hpp
cvconfig.h              objdetect.hpp           text
datasets                opencv.hpp              text.hpp
dnn                     opencv_modules.hpp      tracking
dnn.hpp                 optflow                 tracking.hpp
dnn_superres.hpp        optflow.hpp             video
dpm.hpp                 phase_unwrapping        video.hpp
face                    phase_unwrapping.hpp    videoio
face.hpp                photo                   videoio.hpp
features2d              photo.hpp               videostab
features2d.hpp          plot.hpp                videostab.hpp
flann                   quality                 xfeatures2d
flann.hpp               quality.hpp             xfeatures2d.hpp
freetype.hpp            rapid.hpp               ximgproc
fuzzy                   reg                     ximgproc.hpp
fuzzy.hpp               rgbd                    xobjdetect.hpp
gapi                    rgbd.hpp                xphoto
gapi.hpp                saliency                xphoto.hpp
hfs.hpp                 saliency.hpp
highgui                 sfm
```

### ライブラリファイルの場所

```
$ ls /usr/local/lib | grep libopencv | grep a$ | column -c 80 | expand
libopencv_alphamat.a            libopencv_objdetect.a
libopencv_aruco.a               libopencv_optflow.a
libopencv_bgsegm.a              libopencv_phase_unwrapping.a
libopencv_bioinspired.a         libopencv_photo.a
libopencv_calib3d.a             libopencv_plot.a
libopencv_ccalib.a              libopencv_quality.a
libopencv_core.a                libopencv_rapid.a
libopencv_datasets.a            libopencv_reg.a
libopencv_dnn.a                 libopencv_rgbd.a
libopencv_dnn_objdetect.a       libopencv_saliency.a
libopencv_dnn_superres.a        libopencv_sfm.a
libopencv_dpm.a                 libopencv_shape.a
libopencv_face.a                libopencv_stereo.a
libopencv_features2d.a          libopencv_stitching.a
libopencv_flann.a               libopencv_structured_light.a
libopencv_freetype.a            libopencv_superres.a
libopencv_fuzzy.a               libopencv_surface_matching.a
libopencv_gapi.a                libopencv_text.a
libopencv_hfs.a                 libopencv_tracking.a
libopencv_highgui.a             libopencv_video.a
libopencv_img_hash.a            libopencv_videoio.a
libopencv_imgcodecs.a           libopencv_videostab.a
libopencv_imgproc.a             libopencv_xfeatures2d.a
libopencv_intensity_transform.a libopencv_ximgproc.a
libopencv_line_descriptor.a     libopencv_xobjdetect.a
libopencv_ml.a                  libopencv_xphoto.a
```

## Hello World 画像生成

ソースコード: hello.cpp

```hello.cpp
#include <opencv2/opencv.hpp>

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

  // 画像データを入れるオブジェクト
  cv::Mat image(256, 256, CV_8UC3);

  // テキストを描画する
  cv::String text = "Hello, world";
  cv::Point org(0, 100);
  int fontFace = cv::FONT_HERSHEY_SIMPLEX;
  double fontScale = 1.0;
  cv::Scalar color(0, 255, 127); // Blue, Green, Red
  cv::putText(image, text, org, fontFace, fontScale, color);

  // 画像を出力する
  cv::String path = "hello-world.png";
  cv::imwrite(path, image);

  return 0;
}
```

OpenCV 4 を使うには C++11 準拠のコンパイラが必要。
clang++ でコンパイルする際には -std=c++11 オプションを付ける。

[OpenCV 4\.0](https://opencv.org/opencv-4-0/)

> OpenCV is now C++11 library and requires C++11-compliant compiler. Minimum required CMake version has been raised to 3.5.1.

必要なライブラリファイルを -l オプションで指定する必要がある。

- opencv_core (cv::Mat など基本機能を使う際に必要)
- opencv_imgproc (cv::putText など描画機能を使う際に必要)
- opencv_imgcodecs (cv::imwrite など画像入出力機能を使う際に必要)

hello.cpp のビルドを実行。

```
$ clang++ -std=c++11
-I/usr/local/include/opencv4 \
-L/usr/local/lib/opencv4 \
-lopencv_core -lopencv_imgproc -lopencv_imgcodecs \
hello.cpp
```

生成された a.out を実行すると Hello World 画像が出力される。

```
$ ./a.out 
```

![hello-world.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/8656d7a7-cf4d-1f35-9676-3c906a48c10f.png)

## 画像ファイルをウィンドウに表示

ソースコード: showimage.cpp

```showimage.cpp
#include <iostream>
#include <string>
#include <opencv2/highgui.hpp>

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

  // コマンドライン引数を読む
  if (argc < 2) {
    std::cerr << "画像ファイルのパスを指定してください。" << std::endl;
    return -1;
  }
  std::string path = argv[1];

  try {
    // ファイルから画像を読み込む
    cv::Mat image = cv::imread(path);

    // 画像をウィンドウに表示する
    cv::String winname = path;
    cv::imshow(winname, image);

    // キーが押されるまで待つ
    cv::waitKey();
  } catch (const cv::Exception& e) {
    const char* err_msg = e.what();
    std::cerr << "エラー発生: " << err_msg << std::endl;
    return -1;
  }

  return 0;
}
```

ウィンドウ表示機能 (cv::imshow) を使うので opencv_highgui ライブラリをリンクしてビルドする。

```
$ clang++ -std=c++11 \
-I/usr/local/include/opencv4 \
-L/usr/local/lib/opencv4 \
-lopencv_core -lopencv_imgcodecs -lopencv_highgui \
showimage.cpp
```

生成された a.out を実行すると指定した画像がウィンドウ表示される。

```
$ ./a.out hello-world.png
```

<img width="256" alt="showimage.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/bafe278f-cd7b-eb77-bbb2-c4544a9ae44e.png">

## カメラからの映像をリアルタイムにエッジ抽出

公式ドキュメント [OpenCV: Introduction](https://docs.opencv.org/4.3.0/d1/dfb/intro.html) に載っているサンプルコードをほぼそのまま使用する。

ソースコード: videocapture.cpp

```videocapture.cpp
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace cv;

int main(int, char**) {

  VideoCapture cap(0);
  if(!cap.isOpened()) return -1;

  Mat frame, edges;
  namedWindow("edges", WINDOW_AUTOSIZE);
  for (;;) {
    cap >> frame;

    // 色をグレースケールに
    cvtColor(frame, edges, COLOR_BGR2GRAY);

    // ガウスフィルターで画像をぼかす
    GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);

    // キャニー法によるエッジ抽出
    Canny(edges, edges, 0, 30, 3);

    // ウィンドウを表示
    imshow("edges", edges);
    if(waitKey(30) >= 0) break;
  }

  return 0;
}
```

Video I/O 機能を使うので opencv_videoio ライブラリをリンクしてビルドする。

```
$ clang++ -std=c++11 \
-I/usr/local/include/opencv4 \
-L/usr/local/lib/opencv4 \
-lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_videoio \
videocapture.cpp
```

生成された a.out を実行すると、カメラからの映像をリアルタイムにエッジ抽出したものがウィンドウに表示される。

```
$ ./a.out hello-world.png
```

<img width="960" alt="videocapture.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/3d5daab5-a116-6c05-b3d0-9402b2d41ee6.png">

## 画像から顔の位置を検出

ソースコード: facedetect.cpp

```facedetect.cpp
#include <iostream>
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>

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

  // コマンドライン引数を読む
  if (argc < 3) {
    std::cerr << "画像ファイルのパスを指定してください。" << std::endl;
    return -1;
  }
  std::string input_image_path  = argv[1];
  std::string output_image_path = argv[2];

  // 画像を読み込む
  // アルファチャンネル付きに対応するため IMREAD_UNCHANGED を使う
  cv::Mat input_image = cv::imread(input_image_path, cv::IMREAD_UNCHANGED);

  // Haar 特徴ベースのカスケード分類器による物体検出の準備
  // 顔検出用のカスケード分類器を使用
  std::string face_cascade_name =
    "/usr/local/share/opencv4/haarcascades/haarcascade_frontalface_alt.xml";
  cv::CascadeClassifier face_cascade;
  if (!face_cascade.load(face_cascade_name)) {
    std::cerr << "分類器の読み込みに失敗しました。" << std::endl;
    return -1;
  };

  // 顔を検出
  std::vector<cv::Rect> faces;
  face_cascade.detectMultiScale(input_image, faces);

  // 出力画像データを入れるオブジェクト
  // アルファチャンネル付きに対応するため CV_8UC4 を使う
  // CV_<bit-depth>{U|S|F}C(<number_of_channels>)
  // CV_8U: ビット深度=符号なし8ビット整数(0〜255)
  // C4: チャンネル数=4
  cv::Mat output_image(input_image.size(), CV_8UC4);

  // 入力画像データを出力画像データにコピー
  input_image.copyTo(output_image);

  for (size_t i=0; i<faces.size(); i++) {
    // 検出した顔の座標を出力
    std::cout << "Face: " << faces[i] << std::endl;
    // 顔の位置に楕円を描画
    cv::Point center(faces[i].x + faces[i].width / 2, faces[i].y + faces[i].height / 2);
    cv::Size size(faces[i].width / 2, faces[i].height / 2);
    cv::Scalar color(255, 0, 255, 255); // Blue, Green, Red, Alpha
    double angle = 0;
    double startAngle = 0;
    double endAngle = 360;
    int thickness = 4;
    cv::ellipse(output_image, center, size, angle, startAngle, endAngle, color, thickness);
  }

  // 画像を出力
  cv::imwrite(output_image_path, output_image);

  return 0;
}
```

物体検出機能を使うので opencv_objdetect ライブラリをリンクしてビルドする。

```
$ clang++ -std=c++11 \
-I/usr/local/include/opencv4 \
-L/usr/local/lib/opencv4 \
-lopencv_core -lopencv_imgcodecs -lopencv_objdetect -lopencv_imgproc \
facedetect.cpp
```

生成された a.out の引数に入力画像ファイルパスと出力画像ファイルパスを指定して実行する。

いくつかの画像で試す。

[File:Lenna \(test image\)\.png \- Wikipedia](https://en.wikipedia.org/wiki/File:Lenna_(test_image).png)

```
$ ./a.out lenna.png lenna-output.jpg
Face: [175 x 175 from (215, 202)]
```

![lenna-output.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/693f48f2-74e5-fc0e-6d84-d8a95040e667.jpeg)

[これでも糖質制限ダイエット中の三銃士を連れてきたよ｜無料の写真素材はフリー素材のぱくたそ](https://www.pakutaso.com/20150920253post-6016.html)

```
$ ./a.out 150711109603_TP_V.jpg 150711109603_TP_V-output.jpg
Face: [133 x 133 from (1115, 151)]
Face: [187 x 187 from (681, 116)]
```

![150711109603_TP_V-output.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/1a4a6525-3f9b-cc0e-f7b6-97f39f9bf7d6.jpeg)

[カルビを焼く自分を想像する自分を想像する自分を想像する看護師 \| 看護師フリー写真素材サイト スキマナース](https://nurse-web.jp/photo/archives/279)

```
$ ./a.out cut32_karubi2-1200x801.jpg cut32_karubi2-1200x801-output.jpg
Face: [66 x 66 from (714, 108)]
Face: [97 x 97 from (473, 181)]
Face: [148 x 148 from (98, 273)]
```

![cut32_karubi2-1200x801-output.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/75bc2316-937e-9ab8-3033-4770bce7a28d.jpeg)

[集合している人たちのイラスト（世界） \| かわいいフリー素材集 いらすとや](https://www.irasutoya.com/2019/10/blog-post_448.html)

```
$ ./a.out group_young_world.png group_young_world-output.png
Face: [292 x 292 from (729, 165)]
Face: [295 x 295 from (431, 177)]
Face: [249 x 249 from (997, 354)]
```

![group_young_world-output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/77e00ec0-2e1e-045e-bbe8-e22637a7cf21.png)

## 参考資料

- [OpenCV 4.3.0](https://opencv.org/opencv-4-3-0/)
- [OpenCV: Introduction](https://docs.opencv.org/4.3.0/d1/dfb/intro.html)
- [OpenCV: Basic Drawing](https://docs.opencv.org/3.4/d3/d96/tutorial_basic_geometric_drawing.html)
- [OpenCV: Drawing Functions](https://docs.opencv.org/4.3.0/d6/d6e/group__imgproc__draw.html)
- [OpenCV: Image file reading and writing](https://docs.opencv.org/4.3.0/d4/da8/group__imgcodecs.html)
- [OpenCV: High-level GUI](https://docs.opencv.org/4.3.0/d7/dfc/group__highgui.html)
- [OpenCV: cv::Mat Class Reference](https://docs.opencv.org/4.3.0/d3/d63/classcv_1_1Mat.html)
- [OpenCV: cv::CascadeClassifier Class Reference](https://docs.opencv.org/4.3.0/d1/de5/classcv_1_1CascadeClassifier.html)
- [第1回　OpenCVとは？ 最新3.0の新機能概要とモジュール構成 (1/2)：OpenCV入門【3.0対応】 - ＠IT](https://www.atmarkit.co.jp/ait/articles/1507/27/news154.html)
- [第5回　初めてのOpenCV開発 ― coreモジュール【OpenCV 3.1.0】 (1/2)：OpenCV入門【3.0対応】 - ＠IT](https://www.atmarkit.co.jp/ait/articles/1608/17/news115.html)