Raspberry PiにOpenCV+gstreamerをインストールしH264動画をキャプチャ

# 背景

Raspberry PiでH264動画の動画フレームを取得する手段としてOpenCVを使う。H264動画のデコードをCPUで実行すると時間がかかりすぎるので、GPUでデコードできるようにgstreamerをあわせてインストールする。

# 環境
hardware: Raspberry Pi 3
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 9.9 (stretch)
Release:        9.9
Codename:       stretch

# 手順

gstreamerインストール。
gstreamer1.0-omx-rpiでバージョン指定している理由は後述。

```bash
sudo apt install cmake libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-tools libgtk2.0-dev gstreamer1.0-omx=1.0.0.1-0+rpi12+jessiepmg gstreamer1.0-plugins-bad gstreamer1.0-plugins-good
```

gstreamerがh264のハードウェアアクセラレーションに対応しているか確認する。

```
gst-inspect-1.0 | grep omx
```

以下のように表示される。

```
omx:  omxmpeg2videodec: OpenMAX MPEG2 Video Decoder
omx:  omxmpeg4videodec: OpenMAX MPEG4 Video Decoder
omx:  omxh263dec: OpenMAX H.263 Video Decoder
omx:  omxh264dec: OpenMAX H.264 Video Decoder
omx:  omxtheoradec: OpenMAX Theora Video Decoder
omx:  omxvp8dec: OpenMAX VP8 Video Decoder
omx:  omxmjpegdec: OpenMAX MJPEG Video Decoder
omx:  omxvc1dec: OpenMAX WMV Video Decoder
omx:  omxh264enc: OpenMAX H.264 Video Encoder
```

OpenCVダウンロード。

```bash
mkdir OpenCV
cd OpenCV
wget https://github.com/opencv/opencv/archive/4.1.0.zip
unzip 4.1.0.zip
cd opencv-4.1.0
```

ビルド。
-jオプションの数字を大きくしすぎるとメモリが足りず止まってしまう模様。何度か-jの値を小さくしたり調整した結果ビルド完了まで３～４時間かかってしまった。

```bash
mkdir build
cd build
cmake -D CMAKE_BUILD_TYPE=RELEASE  -D  INSTALL_C_EXAMPLES=OFF  -D WITH_1394=OFF -D WITH_GSTREAMER=ON -D WITH_FFMPEG=OFF -D WITH_QT=OFF ..
make -j 4
```

インストール

```
sudo make install
```

pkg-configが使えるようにopencv4.pcファイルを追加する。

```
sudo mkdir -p /usr/local/lib/pkgconfig
sudo nano /usr/local/lib/pkgconfig/opencv4.pc
```

```txt:opencv4.pc
Package Information for pkg-config

prefix=/usr/local
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include/opencv4

Name: OpenCV
Description: Open Source Computer Vision Library
Version: 4.1.0
Libs: -L${exec_prefix}/lib -lopencv_dnn -lopencv_gapi -lopencv_highgui -lopencv_ml -lopencv_objdetect -lopencv_photo -lopencv_stitching -lopencv_video -lopencv_calib3d -lopencv_features2d -lopencv_flann -lopencv_videoio -lopencv_imgcodecs -lopencv_imgproc -lopencv_core
Libs.private: -ldl -lm -lpthread -lrt
Cflags: -I${includedir}
```


サンプルコードを実行してみる。

```cpp:gstreamer_test.cpp
#include <opencv2/opencv.hpp>

int main(int argc, char *argv[])
{
        cv::VideoCapture gstreamer;
        gstreamer.open("videotestsrc  ! appsink");

        if (!gstreamer.isOpened())
        {
                printf("=ERR= fail to open\n");
                return -1;
        }

        cv::Mat GstCap;
        gstreamer >> GstCap;
        cv::imwrite("test.jpg",GstCap);
        return 0;
}
```

コンパイル＆実行

```
g++ `pkg-config --cflags opencv4`  gstreamer_test.cpp -std=c++11 -g `pkg-config --libs opencv4` -o gstcvtest
 ./gstcvtest
```

test02.jpgとしてカラーバーが表示される。

![test02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189186/157d4bfa-0057-ea44-1354-8b529a60f8db.jpeg)

h264動画をキャプチャしてみる。
使用した動画はここからダウンロード。
https://www2.nhk.or.jp/archives/creative/material/view.cgi?m=D0002040402_00000

```cpp:h264test.cpp
#include <opencv2/opencv.hpp>
#include <iostream>

int main(int argc, char *argv[])
{
        cv::VideoCapture gstreamer;
        //gstreamer.open("filesrc location=/home/pi/nfs/experiment/movie/D0002040402_00000_V_000.mp4 ! qtdemux name=demux demux.video_0 ! h264parse ! omxh264dec ! videoconvert ! appsink");
        gstreamer.open("/home/pi/nfs/experiment/movie/D0002040402_00000_V_000.mp4");

        if (!gstreamer.isOpened())
        {
                printf("=ERR= fail to open\n");
                return -1;
        }


        int i=0;
        cv::Mat GstCap;
        gstreamer.read(GstCap);
        cv::imwrite("nfs/preview.jpg",GstCap);
        while (gstreamer.read(GstCap))
        {
                std::cout << "frame=" <<  i << std::endl;
                i++;
        }
        return 0;
}
```

コンパイル＆実行

```
g++ `pkg-config --cflags opencv4`  h264test.cpp -std=c++11 -g `pkg-config --libs opencv4` -o h264test 
time ./h264test
```

結果。timeコマンドのログ。
1083フレームを37秒なので、29fps程度。

```
real    0m37.376s
user    0m22.739s
sys     0m4.799s
```

# 性能比較

GPUを使わない場合との性能比較を行う。
openの引数に単にファイル名を指定する。

```cpp
 gstreamer.open("/home/pi/nfs/experiment/movie/D0002040402_00000_V_000.mp4");
```

結果。

```
real    0m42.042s
user    0m26.877s
sys     0m4.827s
```

？　あまり変わらない。単純にファイル名を指定した場合でもGPUが使用されているということか。

一度GPU用のプラグインを削除してみる。

```bash
sudo apt remove gstreamer1.0-omx
```

結果。激遅

```
real    7m9.670s
user    7m14.918s
sys     0m0.620s
```

ここでバージョン指定せずにgstreamer1.0-omxを入れてみる。
1.10.4-1+rpt3が入る。

gstreamerパイプラインをopenに記述して実行してみる。

```
real    1m44.353s
user    1m38.168s
sys     0m3.689s
```

このくらい遅い。なぜだろう？


# 参考URL

* http://toriten1024.hatenablog.com/entry/2018/09/29/012205
* https://qiita.com/n0bisuke/items/c786d6afc08e6a0dfb57
* https://qiita.com/kwi/items/36a5233d4ab31c015f8a
* https://qiita.com/vs4sh/items/4a9ce178f1b2fd26ea30
* https://qiita.com/satoyoshiharu/items/72a540a92578faa7929d