GStreamer付きOpenCVをビルドしてほかのパソコンにコピーする(Windows)(py3.xにマルチ対応)

## はじめに
`pip install opencv-python`でインストールできるopencvにはGStreamerがついておらず、これを使ってrtspストリームをVideoCaptureするとラグがひどくてとてもリアルタイムとは呼べません。そこでGStreamerを使うために自前でビルドする必要が出てきますが、ビルドを完了すると**20～30GBくらいディスクを食われます**。こんなのよくないし、他のマシンにコピーだってできません。
この記事では、ビルドしたopencv(cv2フォルダ)を**コピーするだけで他のマシンでも使用でき**、ビルドに使ったファイルを削除しても問題ないように仕上げます。最終的に600MBくらいになりました。もっと詳しい人ならもっと小さくできるかもしれません。
また、僕の身の回りではpythonバージョンが乱立してるので、マルチバージョン対応(といってもそれぞれのバージョン用のビルドするというやっつけ仕様だが)にします。(ラップトップがPython3.9、仕事用PCには3.10、自宅用PCには3.11がインストールされちゃってます)

## ビルドに必要なものの入手
以下を用意します。入手したzipは展開してください。個人的なおすすめは、CドライブにDevelopなどという名前のフォルダを作り、この中に用意したファイルを入れておきます。
- CMake：https://cmake.org/download/ より、make-x.xx.x-windows-x86_64.msiをDL。普通にインストールします。
- OpenCV：https://opencv.org/releases/ より、最新版のSourcesをDL
- OpenCV_contrib：https://github.com/opencv/opencv_contrib/tags よりOpenCVと同じバージョンをDL
- GStreamer：https://gstreamer.freedesktop.org/download/ よりWindowsタブ->x.xx.x runtime installerとx.xx.x development installerをDL。次でインストールします。
- (Visual Studio 2019：ビルドに必要です。)

## gstreamerのインストール
runtimeもdevelopも、Completeでインストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/681aeb9d-fbd3-e66b-033f-10729ccfc6ad.png)


## CMakeの設定
1. OpenCVのパスを設定
1. ビルド先のパスを設定(どこでもいい)
1. Configureを押下
1. 出てきたウィンドウではそのままFinishをクリック
1. 以下のようにパラメーターを設定(適宜Searchを使うとよいでしょう)：
OPENCV_EXTRA_MODULES_PATH=opencv-contribのmodulesフォルダ
BUILD_opencv_world=ON（なんとなく）
BUILD_SHARED_LIBS=OFF（とても大事(依存関係を一緒に含める)）
GSTREAMER_～～をすべて設定(スクショ参照)
OPENCV_GAPI_GSTREAMER=ON（大事）
WITH_GSTREAMER=ON（大事）
**PYTHON3_PACKAGES_PATH=C:/Develop/SiTePaCk**（できるだけ変な名前/分かりやすい名前にする）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/b5db199b-910f-8f6b-9d15-37d325e9bdf4.png)
今回は、ソースもビルド先もD:/Develop/に置いてみました。SSD(Cドライブ)の空き容量に不安があったので大容量HDD(Dドライブ)にしただけで、深い意味はないのでみんなはCドライブでいいと思います。gstreamerがDドライブにインストールされているのはたまたまで、通常はCドライブにインストールされるはずです(つまりD:/gstreamer/～をC:/gstreamer/～に読み替えてください)。
**PYTHON3_PACKAGES_PATH=C:/Develop/SiTePaCk**と変な名前にする理由は、デフォルトだと現在アクティブなpythonのsite-packagesにインストールされてしまうからです。うっかりpip版のopencvが入っていると競合しそうなので、変な名前に設定することでそれを明示的に回避します。
1. Configureを押下、その後Generateを押下(configureはしょっちゅう押してもいいらしい)
1. 下の画面でビルドオプションを確認する。ダメそうなら設定を見直す。
Video I/OのGStreamerがYESになっていること
Python 3 のinstall path が変な名前のフォルダ/cv2/python3.x のようになっていること
1. Open Projectを押してVisual Studioを開く
1. Releaseにする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/18456790-d3ca-13e0-7f39-93f852c5c671.png)
1. ビルド>ビルド>ALL_BUILDとINSTALL をビルドする(1時間はかかる)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/5998225a-d0ef-09f5-9b45-728a24d2b982.png)

## 使えるようにする
ビルドが正常終了すると、ビルド先のフォルダにたくさんのデータが(数十GB)、C:/Develop/SiTePaCk(変な名前を付けたフォルダ)に`cv2`フォルダができているはずです(80MBくらい)。ビルド先のフォルダの中身はほとんど使いませんが、せっかくなのでbinフォルダの中の`opencv_videoio_ffmpeg4xx_64.dll`をcv2フォルダにコピーしておきます。以下の記述はcv2フォルダについてのものです。
僕の環境では(仕事用PCのPython3.10でビルドしたため)、この中に`config-3.10.py`と`python-3.10`フォルダの中に`cv2.cp310-win_amd64.pyd`が居ました。ここからはpython3.10として書いていますが、皆さんのバージョンに合わせて適宜**3.xと読み替えてください**。
> このパソコンのみで使用する場合はこのままで良いのですが、マルチバージョンにしたい場合は、新たに`python-3`フォルダ(名前はなんでもよい)を作り、その中に`cv2.pc3x-(略).pyd`を移動させ、紛らわしいので`python-3.x`フォルダは削除します。

ここで、コントロールパネル>プログラムのアンインストールから、`GStreamer 1.0(Development Files)`をアンインストールします(デカくて邪魔なので)。`GStreamer 1.0`はまだ残しておきます(これは必要です)。
> マルチバージョンにしたい場合は、まだDevelopmentを使用するので先にビルドをしたほうがいいです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/dec01f73-4770-5de5-c8ef-1479f181a171.png)
`cv2/python3.10`フォルダに、`C:/gstreamer/1.0/msvc_x86_64/bin`と`C:/gstreamer/1.0/msvc_x86_64/lib`をフォルダごとコピーします。
フォルダ構造は次のようになります(一部抜粋)：
```
└ SiTePaCk/
  └ cv2/
    ├ python-3.10/ 
    | ├ bin/
    | ├ lib/
    | └ cv2.cp310-win_amd64.pyd 
    ├ config-3.10.py
    └  ...
```
(ここまでできたら、ビルドしたマシンでは動作を確認できます。)
続いて、cv2ディレクトリの`config-3.10.py`の内容を以下のように編集します。
```py:config-3.10.py
import os
file_path=__file__　#このファイルのパスを取得
dir_path=os.path.dirname(file_path) #このファイルのあるフォルダまでのディレクトリ

PYTHON_EXTENSIONS_PATHS = [
    os.path.join(dir_path, 'python-3.10') #相対パスに直す
] + PYTHON_EXTENSIONS_PATHS

BINARIES_PATHS = [ #gstreamerをコピーした理由はポータブルにするため
    os.path.join(dir_path, 'python-3.10', 'bin')
] + BINARIES_PATHS
```
> マルチバージョンにしたいときは、config-3.pyなどを新たに作り、これをコピペして'python-3'に直します。
> また、`__init__.py`に
> `'config-{}.{}.py'.format(sys.version_info[0], sys.version_info[1]),` という行があるのですが、これは現在実行中のpythonバージョンに合わせて参照ファイルを変えていますので、これを
> `'config-3.py',`にしちゃいます。
> ここで気を付けなければならないのが、後から別バージョン用にビルドすると、`__init__.py`が書き換えられてもとに戻ってしまうので、ビルド後には確認するようにしてください。
> config-3.pyができれば、もうconfig-3.10.pyはいらないので消しちゃいましょう。

<details><summary>ここでcp932コーデックが云々というエラーが出たので直します。本編とは関係ないです</summary>

なんか意味不明なエラーが出たので直します
```
File "path\to\cv2\load_config_py3.py", line 8, in exec_file_wrapper
    code = compile(f.read(), os.path.basename(fpath), 'exec')
                   ^^^^^^^^
UnicodeDecodeError: 'cp932' codec can't decode byte 0x86 in position 22: illegal multibyte sequence
```
こうやって直すらしい
control.exe -> 地域 -> [管理]タブ -> システム ロケールの変更 -> ベータ: ワールドワイド言語サポートでUTF-8を使用

<img width=39% src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/c36a7e86-781d-b849-f616-a67c31740a91.png">
<img width=39% src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/a51c540b-7b8c-2617-81bf-bc93eae442be.png">
<img width=20% src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/c94b2e87-bb82-761f-f15f-2af075dbc3f0.png">

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/87035fdc-d5d7-249f-06c8-fc9fc7a3d7c4.png)
参考

https://qiita.com/misohagi/items/e26c589506a69261fe04

</details>

## 使ってみる
自前でビルドしたはいいものの、依存関係がないと動かないと思うので、いったん普通に`pip install opencv-python`をしたほうがいいと思います。
`site-packages`の`cv2`フォルダは、僕はとりあえず`cv2-original`とリネームして保持しておきました。
`site-packages`に自前でビルドした`cv2`フォルダをコピーすれば完成です。普通にコマンドプロンプトからpythonを起動し、

```py
import cv2
print(cv2.getBuildInformation())
```
などで動作を確認してみます。実際に
```py
cap=cv2.VideoCapture("rtspsrc location=rtsp://ip-address:port latency=0 ! decodebin ! videoconvert ! appsink", cv2.CAP_GSTREAMER)
```
ができることを確認しましょう。もし動かない場合はProcess Monitorが便利です。

## マルチバージョンにする
僕の環境では自宅用PCでもラップトップでも動作させたいので、追加でpython3.9用とpython3.11用にもビルドします。このとき、gstreamerのdevelopmentをアンインストールしちゃった人はもう一回インストールしましょう。
ビルドするマシンにターゲットとなるバージョンのpythonをインストールしましょう。(ここで多分3.9.y、3.11.yなどの孫バージョンは問わないと思います。)
https://www.python.org/downloads/windows/ から目的のバージョンのWindows installer (64-bit)をダウンロードします(embedじゃダメです。見つからない場合はCtrl+Fを使いつつ、下にスクロールしてみましょう。)

ビルド用に一時的にインストールするだけなので、Customize installationを選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/1ce82e23-d89c-5533-7a27-0c8b51d64b47.png)
いらないのでチェックは全部外しちゃいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/7acabca8-40e2-b3cf-0e18-f7d778cdff42.png)
次の画面でもチェックを全部外し、インストール場所を好きな場所(C:/Develop/ など)にします。(たまたま今回はDドライブにしましたが、みんなはCドライブで良いと思います。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719521/af0cce01-6774-6ad9-64fd-fbe9ed64a8e4.png)

次にcmakeの設定を別のバージョンのpython用に書き換えます。
いったんcmakeを終了させておきます。
ビルドフォルダ(C:/Develop/BUILD_410GST など)にある`CMakeCache.txt`を開き、pythonのバージョンが書いてあるところを修正していきます。
例えば`C:/Users/ユーザー名/AppData/Local/Programs/Python/Python310/python.exe`を`C:/Develop/Python311/python.exe`に変えたり、
`Python310/include`を`Python311/include`に、
`Python310/libs/python310.lib`を`Python311/libs/python311.lib`に、
`3.10.5`と書かれているところを`3.11.9`にしたりします。
エディタの検索機能で`ython310`や`3.10`などで検索し、必要に応じて`ython311`や`3.11`、`3.11.9`などにします(大文字小文字区別のないp'ython'で検索すると良いでしょう)。一つ注意なのは、**numpyのincludeディレクトリのパスに`Python310`が入っていてもここは変更しないでください**。numpyのバージョンはそろえておきます。

全部できたらCMake GUIを起動し、Configureを押下します。下の画面(⑦)のPythonの項目で目的のPythonバージョンになっているか確認したらGenerateしてOpen Project、ALL_BUILDとINSTALLをビルドします。2回目なので1回目ほど時間はかからないはずです。

この作業を目的のpythonバージョン分繰り返し、目的の`cv2.cp3x-win_amd64.pyd`ができたら`python-3`フォルダにコピーしちゃいます。`__init__.py`が毎回もとに戻されてしまうので、
```py
'config-{}.{}.py'.format(sys.version_info[0], sys.version_info[1]), 
```
を
```py
'config-3.py',
```
にしちゃいます。