Pythonが嫌いなのでC++版のPytorchで画像認識をやってみる 16日目 C++でYolov8 ソース晒す

# ソース公開
これ以上いじると訳が分からなくなると思うので、機能がシンプルなうち一度晒しておく。監視カメラのサイネージを意識して作ったので、似たような目的に使うならソコソコ実用的かもしれない。通行人のカウントとか駐車場の車の数とか、簡単に数値化できる。
今のところ晒すのソースだけ。バイナリは各OSSのライセンスを調べてから。
![スクリーンショット 2023-08-20 145019.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464633/cc3ed8ff-e159-7a80-f547-303046b31b65.png)

**[ソースはここ](https://github.com/pipimaru1/StreamViewerAI2.1)**
偉そうにいろいろ書いておいて、外部設計とかしておらず、素人丸出しのコード。実際プロではないので、まあ勘弁して頂戴。リポジトリ名、プロジェクト名、フォルダ名が微妙に合っていないけど気にせぬよう。gitもvsも使い方がまだイマイチ分からんので、こちらも勘弁。

# 機能
* IPカメラやUSBカメラの映像をキャプチャーして、AI解析して表示。
* Yolov5とYoloV8のonnxに対応。
* 画面のダブルクリックで全画面表示。
* マルチディスプレイ対応。
* AIの解析結果をCSVファイル(ai_proccessed.csv)に保存。
* 各社IPカメラ、USBカメラに対応。複数指定可能、自動切換え。
* スペースキーでカメラ切替。
* CUDA対応。

# Buildの方法
バイナリはまだ公開できないので、必要なOSSは各自で用意して、インクルードとリンクして頂戴。
**OpenCV**
・バージョンは4.5.2 または 4.7.0 または 4.8.0。
・4.5.2でビルドすると、Yolov5のみの対応になる。Yolov8には4.8.0が必要らしい。
・Yolov8をGPU(CUDA)で動かすには4.7.0のCUDA版Opencvが必要。各自、自分でビルドのこと。
・4.7.0のCPU版ではYolov8は動かなかった。
|OpenCV|CUDA|Yolov5|Yolov8|
|---|---|---|---|
|4.5.2|-|〇|×|
|4.5.2|CUDA|〇|×|
|4.7.0|-|〇|×|
|4.7.0|CUDA|〇|〇|
|4.8.0|-|〇|〇|
|4.8.0|CUDA|〇|×|

※OpenCV4.8.0のCUDA版のビルドがまだ成功していないので…
⇒ビルド成功、Yolov8モデルの~~動作可能~~。⇒ダメでした。

**CUDAとCUDNN**
・公式からCUDA 11.8とそれに合うCUDNNをインストールして、環境変数をセットする。
・CUDAのバージョンはLibtorchに合わせておければOKと思う。

**Libtorch**
・2.0.1をダウンロードして配置、リンク。
![スクリーンショット 2023-08-20 155100.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464633/f04d7130-ffc2-c394-abc5-e57ee4b5f32d.png)
デバッグ版、リリース版、CPU版、CUDA版の4種類ある。オジサンは全部入れてセットした。

**Onnxruntime**
Visual StudioのNugetから最新版を入れておけばいいんじゃなかろうか。

# 使い方
* 何も指定せず起動すると、起動フォルダにある**default.ini**を読み込み、表示する。
* default.iniの中で、**cams.txt、yolov5s.onnx、coco.names**を指定している。これらのファイルは下記のようにコマンドラインからも指定できる。
* cams.txtに書いてあるIPカメラ、WEBカメラ、USBカメラの映像を受信し、順番に表示する。デフォルトは30fpsで30秒ごとに切替。
* **yolov5s.onnx、coco.names**は物体識別に必要なファイル。
* yolov5s.onnxは自分で用意のこと。公式のyolov5s.ptファイルから変換する場合は、opset=12を指定。
* yolov5sだけではなくyolov5xやyolov5x6も読み込み可。勿論、自作した学習モデルも読み込み可。
* 自分で作ったカメラリストを読み込ませる場合は起動時にコマンドライン引数で指定する。
```
StreamViewerAI2.exe your_cams.txt 
```
* 独自のonnxファイルを指定する場合は下記のようにonnxファイルとnmamesファイルを指定する。AIモデルのクラス数とnamesのオブジェクト数が合ってないと、ランタイムエラーで死ぬ。
```
StreamViewerAI2.exe your_cams.txt your.onnx your.names 
```
* カメラファイル(your_cams.txt)の書き方
こんな風に書けばOK。resolutionやフレームレートは指定しなくても可。ただし指定したほうがカメラ側の負担が減る場合もある。ちなみに640以上の解像度を指定しても、内部で640に落としている。
```
カメラ名1,http://hoge1/cgi-bin/mjpeg?resolution=640x480
カメラ名2,http://hoge2:63101/cgi-bin/mjpeg?resolution=640x480
USBカメラ,0
```

* namesファイルの書き方
↓こんな風にオブジェクト名を書いておく。UTF8にしておけば無難かと。オブジェクト名の数はAIモデルの出力サイズに合わせること。オブジェクト名の数=オブジェクト数になる。
```
person
bicycle
bus
```

# 実行
下記のようにファイルがそろっていれば動くはず。
[2024/3補足] 
* こんなにファイル要らなかった。基本的にはOpenCVのdllがあれば動く。cudaとcudnn、onnx、libtorchのランタイムは不要なので、ヘッダとライブラリから外してOK。
* cudaとcudnnはopencvのビルドの時に組み込まれるので、exeを作る段階では不要。dllも要らない。

**Releace**
```
asmjit.dll
c10.dll
cams.txt
coco.names
fbgemm.dll
fbjni.dll
libiomp5md.dll
libiompstubs5md.dll
opencv_videoio_ffmpeg480_64.dll
opencv_videoio_msmf480_64.dll
opencv_world480.dll
pytorch_jni.dll
StreamViewerAI2.exe
StreamViewerAI2.exp
StreamViewerAI2.lib
torch.dll
torch_cpu.dll
torch_global_deps.dll
uv.dll
yolov5s.onnx
```
**Releace(CUDA)**
```
asmjit.dll
c10.dll
c10_cuda.dll
caffe2_nvrtc.dll
cams.txt
coco.names
cublas64_11.dll
cublasLt64_11.dll
cudart64_110.dll
cudnn64_8.dll
cudnn_adv_infer64_8.dll
cudnn_adv_train64_8.dll
cudnn_cnn_infer64_8.dll
cudnn_cnn_train64_8.dll
cudnn_ops_infer64_8.dll
cudnn_ops_train64_8.dll
cufft64_10.dll
cufftw64_10.dll
cupti64_2022.3.0.dll
curand64_10.dll
cusolver64_11.dll
cusolverMg64_11.dll
cusparse64_11.dll
fbgemm.dll
fbjni.dll
hdf5.dll
libiomp5md.dll
libiompstubs5md.dll
nvfuser_codegen.dll
nvrtc-builtins64_118.dll
nvrtc64_112_0.dll
nvToolsExt64_1.dll
opencv_videoio_ffmpeg480_64.dll
opencv_videoio_msmf480_64.dll
opencv_world480.dll
opencv_world480.pdb
pytorch_jni.dll
StreamViewerAI2.exp
StreamViewerAI2.lib
StreamViewerAI2Gpu.exe
uv.dll
yolov5s.onnx
zlibwapi.dll
```


# 既知のバグ
CUDA + Yolov8 +OpenCV4.8.0 の組み合わせがうまくいっていない。AIは動くのだが、位置とサイズがすべてゼロになる。OpenCV4.8.0のCUDA版のビルドがうまくいっていないので、それのせいか? なぜかCPUでは動かない4.7.0のCUDAでは動く。

カメラから応答がないと、そこで停止してしまう。というかずっと待ち状態。cv::capture()の中で止まってしまうので、今のところどうしようもない。captureを起動する前に死活チェック入れれば多少はマシになるかも。

描画中に画面の端をドラッグしてサイズ変更すると「応答なし」になる可能性。
WM_PAINTイベントで描画スレッドの生成と廃棄をしているためで、ドラッグしてサイズ変更すると、大量のスレッド生成・廃棄処理が行われるため。


# 独り言
1週間連続稼働しても動いているので、まあまあ安定して動いているんじゃなかろうか。
Pythonと違ってUIが自由自在なのかC++の良いところ。監視カメラのサイネージ用に作ったアプリだけど、画像認識AIでやりたいことは大体出来るんじゃなかろうか。手軽にAIを楽しめると思う。CSVも吐くので、BIで解析したりするのも面白い。バイナリ版を公開できれば、ダウンロード一発で動かせるのだけど、OSSの権利関係がよくわからん。きついのはGPLv3のYOLOくらいかなあ。

つづく