M1 Macでncnnを使ってYOLOXを動かしたメモ
M1 Macでncnnを使ってカメラからの入力をYOLOXに入れて遊んでみたら意外と詰まるところがあったのでメモ。基本的にハマったところしか書いていません。
きっかけ
インターネットを眺めていたらncnn(https://github.com/Tencent/ncnn)という面白そうなものを見つけたので、とりあえず面白そうなYOLOXを動かしてみた。
せっかくなのでLinuxではなくmacOSで動かしてみてmacOSの開発環境にも触ってみることにした。
環境
- MacBook Pro 14インチ (2021) & M1 Pro
- macOS 12.4
- Xcode 13.4.1
(どうでもいいですけどmacOSのバージョンとXcodeのバージョンってずれるんですね)
本編
OpenCVをインストール
サンプルコードがopencvを呼んでいたので素直に使うことにした。OpenCVを使わずにncnn&YOLOXができるかどうかは不明。
今回はOpenCVをHomebrewでインストールする。Homebrewが導入されていない場合は事前に導入しておく。
brew install opencv
Xcodeでプロジェクト作成
C++でコードを書くため、XcodeのテンプレートはmacOS
のCommand Line Tool
を選択し、LanguageでC++
を選ぶ。
OpenCVを使うためにビルド設定を変更していく。
まず、Xcodeのファイル一覧画面で一番上のプロジェクト名の行を選択し、Build Settings
タブを開く。
Search Paths
セクションの中のHeader Search Paths
とLibrary Search Paths
を設定するため、以下のようにpkg-config
コマンドを実行してopencv4のパスを確認する。
$ pkg-config --cflags opencv4
-I/opt/homebrew/opt/opencv/include/opencv4
$ pkg-config --libs opencv4
-L/opt/homebrew/opt/opencv/lib -lopencv_gapi -lopencv_stitching -lopencv_alphamat -lopencv_aruco -lopencv_barcode -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_dnn_objdetect -lopencv_dnn_superres -lopencv_dpm -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hfs -lopencv_img_hash -lopencv_intensity_transform -lopencv_line_descriptor -lopencv_mcc -lopencv_quality -lopencv_rapid -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_sfm -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_superres -lopencv_optflow -lopencv_surface_matching -lopencv_tracking -lopencv_highgui -lopencv_datasets -lopencv_text -lopencv_plot -lopencv_videostab -lopencv_videoio -lopencv_viz -lopencv_wechat_qrcode -lopencv_xfeatures2d -lopencv_shape -lopencv_ml -lopencv_ximgproc -lopencv_video -lopencv_xobjdetect -lopencv_objdetect -lopencv_calib3d -lopencv_imgcodecs -lopencv_features2d -lopencv_dnn -lopencv_flann -lopencv_xphoto -lopencv_photo -lopencv_imgproc -lopencv_core
今回は上記の結果になったため、
Header Search Paths
= /opt/homebrew/opt/opencv/include/opencv4
Library Search Paths
= /opt/homebrew/opt/opencv/lib
次に、リンク設定を変更するためLinking
セクションのOther Linker Flags
にpkg-config
の結果の-l
から始まる部分全てを入れる。
これでこのプロジェクトでOpenCVが使えるようになる。
ncnnをダウンロード
ncnnの自前ビルドはせず、ncnnのGitHubのReleaseからビルド済みのバイナリをダウンロードして使う。
今回使ったファイルは ncnn-20220420-macos.zip
GPUも使うためにはVulkanとついている方になるが、今回はとりあえず動かすことが目的なので、CPUのみのバイナリを使うことにした。
ncnnをプロジェクトに追加
(結局正しい方法はよくわからなかったので間違っているかもしれない)
ncnnのReleaseのzipをダウンロードして展開すると、ncnn.framework
とopenmp.framework
の2つのフォルダが出てくる。
このframeworkフォルダはどうやらXcodeの機能でヘッダとバイナリをまとめたものらしい。
Xcodeのファイル一覧画面で一番上のプロジェクト名の行を選択し、General
タブのFrameworks and Libraries
セクションで+を押し、Add Other -> Add Filesから上記の2つのframeworkフォルダを選択すると追加される。
デフォルトではEmbed & Sign
になっているが、ビルド時にエラーになったので適当に動かすだけならEmbed without Signing
にする。
これで本来ならframeworkフォルダに入っているヘッダファイルも使えるようになるようだが、自分の環境では参照エラーが出てビルドが通らなかった。そのため、Header Search Paths
にncnn.framework/Versions/A/Headers/ncnn
を追加してエラーを回避した。(多分間違った対処法)
コーディング
ncnnリポジトリのexamplesからYOLOXのものを持ってくる。
https://github.com/Tencent/ncnn/blob/master/examples/yolox.cpp
カメラからの映像を入力にしたいので、main
を少しだけ書き換えこんな感じにする。
cv::Mat frame;
std::vector<Object> objects;
// カメラを開く
cv::VideoCapture cap(0);
// 映像を取得してYOLOXに渡してobjectsを四角で囲った画像を表示する
cap.read(frame)
detect_yolox(frame, objects);
draw_objects(frame, objects);
モデルを配置する
絶対にビルド時に自動的にコピーさせる方法があるが、調べるのが面倒くさかったため、ビルドフォルダに手動でモデルをコピーしてお茶を濁した。
ビルドしたファイルが置かれるフォルダは以下の通り
~/Library/Developer/Xcode/DerivedData/プロジェクト名-ランダムな文字列?/Build/Products/Debug
ここにyolox.param
とyolox.bin
を置くと動く。
モデルはncnn用に変換されたものをどこかから持ってくるか、YOLOXのリポジトリからONNXファイルを持ってきて、ncnnのtoolsに入っているonnx2ncnn
を使って変換する。
https://github.com/Megvii-BaseDetection/YOLOX/tree/main/demo/ONNXRuntime
https://yolox.readthedocs.io/en/latest/demo/ncnn_cpp_readme.html
自分で学習させたモデルをONNXに変換してncnnで使うこともできるらしい。
ちなみにonnx2ncnn
などのツールはLinuxのprebuildには入っているが、macOS版には入っていないような気がする。ちょっと悲しい。