Repository navigation
OpenCV 5.0.0 を Unity 6000.3 以降向けに、独自の C ABI と C# API で提供する native UPM パッケージ。
導入
全部入りの tarball を 1 つ入れる。 com.ayutaz.opencv-unity-native.tgz に
Windows x64 / macOS arm64 / Linux x64 / Android arm64-v8a / iOS arm64 / Web (WebGL)
の binary が 6 つとも入っており、Unity は自分の platform 向けだけを読み込む
(Plugin Import Settings がそう決めている)。
manifest.json に同じ package ID は 1 回しか書けないので、platform ごとに分かれた
tarball では「エディタは Windows、実機は Android」が表現できない。これが全部入りを
正にした理由である。
Android と iOS は実機で一度も動かしていない。 CI はクロスビルドし、16 KB page
size と束ねたシンボルを機械的に検査しているが、どの端末もこの binary を読み込んだ
ことがない。 詳しくは下の「この版で確かめていないこと」を読むこと。
platform ごとの tarball(com.ayutaz.opencv-unity-native-<version>-<platform>.tgz)も
引き続き付けてあるが、1 platform 分だけでよい場合の補助であって正ではない。
相対パスは Packages フォルダからの解決になる。絶対パスでもよい。詳しくは README の
Installing を参照。
Git URL では導入できない。 native plugin の binary は git の追跡外にあるため、
Git URL で参照しても .meta しか届かず、DllImport が実行時に全部失敗する。
この tarball を使うこと。
DNN を使う場合(この版で追加)
ONNX の推論は opt-in である。 既定では C# の API が存在しない。使うには
Project Settings → Player → Other Settings → Scripting Define Symbols に
OCVU_PROFILE_DNN を足す。Unity の define は build target ごとに持つので、
対象にする platform すべてに足すこと —— エディタの platform にだけ足すと、
Editor では動くのに Android や iOS のビルドで CvDnn が見つからなくなる。
package.json の versionDefines では自動化できない。あの仕組みは
別の package が在ることを条件に define を立てるものだが、dnn は
この package の中に在るので、条件にするものが無い。利用者が自分で立てる。
native の binary は既定で dnn を含んでいる —— 切っているのは C# 側の
assembly(CvUnity.Interop.Dnn / CvUnity.Dnn)だけである。したがって
define を足しても binary を差し替える必要は無く、逆に足さなければ
CvDnn / CvNet は利用者のビルドから丸ごと消える(参照が壊れた状態は
残らない)。
公開クラスの namespace は CvUnity.Dnn である(CvMat などは CvUnity、
Unity 連携だけ CvUnity.Unity なので、using CvUnity.Dnn; が別に要る)。
engine / backend を選ぶ引数も定数も出していない。実質 CPU のみである。
OpenCV 5.1 で enum EngineType が総入れ替えになる見込みなので、5.0 の値を
この境界に固定しない。
この経路は実機で一度も動かしていない。 下の「この版で確かめていないこと」を読むこと。
検証
この全部入りパッケージを、使い捨ての Unity プロジェクトへ導入して
確かめてある —— 公開前にこの asset そのものを落として展開し、
6 platform 分を材料に、同じ script(pack-upm-tarball.ps1)で固め直して導入した。6 つ入った状態で Unity が読み込み、
自分の platform 向けだけを有効にするところまで見ている
(native plugins present: 6 と EditMode が緑になるところまで)。
テストの件数はここに書かない —— この本文は次のリリースでも読まれるので、
検査を 1 件足した日にこの行だけが嘘になる。
「中に 6 つ入っている」とは別の主張である。
この版から、同じ検証を CI も毎回行う。 pull request のたびに、配る形の
tarball を使い捨ての Unity プロジェクトへ導入し、UPM がディレクトリ参照ではなく
.tgz で解決したことまで確かめている。それでも公開前に人が 1 回回すのは、
CI が固めるのが「その時点の binary」であってこれから配る asset ではない**からである。
全 asset の SHA-256 は SHA256SUMS.txt にある。
sha256sum -c SHA256SUMS.txt # Linux
shasum -a 256 -c SHA256SUMS.txt # macOS落としていないファイルは missing と出る(想定どおり)。落としたものが OK であればよい。
全部入りの checksums.txt(接頭辞なし)と、各 platform の <platform>-checksums.txt は
package の中身を対象にしている。各行は package/ からの相対パスなので、
展開してできる package/ の中に入って使うこと —— 展開先の root で走らせると
6 件とも No such file or directory になる(実測)。
tar -xzf com.ayutaz.opencv-unity-native.tgz
cd package && sha256sum -c ../checksums.txtSHA256SUMS.txt は逆に、ダウンロードする物そのものを対象にしている。
前の版(v0.3.0)から変わったこと
公開している C ABI は 27 本から 57 本になった。 内訳は下のとおりで、
OCVU_ABI_VERSION は 1 のまま変わっていない(関数の追加は bump しない変更である)。
下は C# のクラスで並べてある。 C ABI の module 名とは一致しない ——
たとえば GetPerspectiveTransform は ABI では geometry だが、C# では
WarpPerspective と対で使うので CvOps に在る。
- 画像処理が 9 本増えた(
CvOps)—— 2 値化(Threshold)、Canny のエッジ検出、
モルフォロジー(MorphologyEx)、4 点対応からの射影変換行列
(GetPerspectiveTransform)とその適用(WarpPerspective)、
テンプレート照合(MatchTemplate)、輪郭抽出(FindContours)、
確率的 Hough 直線(HoughLinesP)、コーナーの副画素精度化(CornerSubPix) - 基本演算が 8 本増えた(
CvCoreOps)—— channel の取り出しと差し込み、
最小・最大とその位置、範囲内判定(InRange)、正規化、ビット演算、
ルックアップテーブル(Lut)、余白の追加(CopyMakeBorder) - 姿勢推定が入った(
CvGeometry)——SolvePnP、ProjectPoints、
Rodrigues の相互変換 2 本の計 4 本。
前の版で入った校正(内部パラメータと歪み係数)と合わせて、
「校正して、姿勢を求めて、投影する」までが繋がった - ArUco マーカーを読み書きできる(
CvAruco)—— 生成と検出に加えて、
マーカーの姿勢推定(EstimateMarkerPose)。3 つ目は新しい C ABI 関数を
使わず、SolvePnPの上に立つ純 C# である - 特徴量の記述子と照合(
CvFeatures)——DetectAndComputeとMatchDescriptors - ステレオの視差(
CvStereo.ComputeDisparity)。新しい module である
(OpenCV のstereoをこの版から実際にリンクしている) - ONNX の推論(opt-in)(
CvDnn/CvNet)—— ONNX をメモリから読み、
blob を作り、forward を 1 回走らせる 4 本。OCVU_PROFILE_DNNを立てた
ときだけ C# 側に現れる(上の「導入」を参照)
Unity 連携が 1 つ増えた。
RenderTextureからCvMatを作れる(CvUnity.Unity.RenderTextureConverter)。
同期のToMatと、AsyncGPUReadbackを使う非同期のRequestMatがある。
新しい C ABI 関数は使っていない —— 既存の上に立つ純 C# である。
WebCamTextureConverterと同じ規約で常に上下を反転する
(Unity は左下原点、OpenCV は左上原点)。WebCamTextureConverterと違い、
反転を切る引数は無い ——Texture2Dへ戻すなど反転が不要な用途では、
呼ぶ側で戻すことになる
Android の .so からデバッグ情報を落とした。
これは「dnn を足したら重くなったので削った」ではない ——
v0.3.0 まで、Android の .so はデバッグ情報を積んだまま配られていた
(99,463,016 バイトのうち 82% が .debug_*)。dnn はそれを配布上限の
向こう側へ押し出しただけで、原因ではない。
llvm-strip --strip-unneeded の効果は、Android の .so が
258,995,040 → 24,385,832 バイト、全部入り tarball が
124,102,343 → 77,528,652 バイト(strip の有無だけを変えて測った)。
この版の実物は 77,530,189 バイト(約 74 MB)である。 直前の
77,528,652 は strip の効果を測るための再現なので、配る物そのものの
大きさとは別に書いてある —— ビルドごとに数十バイト動く。
**v0.3.0 の実物は 69,565,901 バイト(66 MB)**なので、dnn をまるごと足しても
増加が約 8 MB で済んでいるのは、この strip が相殺しているからである。
代償は明確である。 .dynsym は残るので動作と 16 KB page size への対応には
影響しないが、.symtab / .strtab が無いので、実機で native crash が起きても
関数名を復元できない(ndk-stack や Crashlytics の native シンボル化が効かない)。
symbolicate 用の debug package はどこにも発行していないので、利用者が自分で
取り戻す経路も無い。 Android は実機で一度も動かしていない以上、最初に
crash を踏むのは利用者である可能性が高い。他の 5 platform は変えていない。
性能の実測値をこの版から公開している —— 境界のコピー、Texture2D /
RenderTexture の経路、起動時間(ただしこの数字は「native ライブラリの
真の初回ロード」を捉えていない。理由はリンク先にある)を、開発機と CI の
2 つの環境で測った数字が
性能
にある。時間は公開するが、速い・遅いで CI を落とすことはしない(共有
ランナーの上で閾値を置くとフレークになるため)。
退行(Android / iOS / macOS arm64 を使う人は必ず読むこと)
PNG のデコードとエンコードが、Android・iOS・macOS arm64 の 3 platform で
ARM 加速を丸ごと失った。 dnn を使うかどうかに関係なく、
これらの platform で CvCodecs を使う利用者全員に影響する。
理由は上流 OpenCV 5.0.0 の vendoring の欠陥である —— mlas が実体の無い
シンボルを無条件に参照しており、macOS arm64 のリンクが落ちる。その経路を
止めるには libpng の ARM NEON 向け enable_language(ASM) を止めるしかなく、
PNG_ARM_NEON=off はアセンブリで書かれたカーネルだけでなく、同じ分岐に
ある NEON intrinsics の実装も道連れにする(上流が両方を 1 つのスイッチに
まとめているため)。
これは取引であって、無条件の勝ちではない。 JPEG の SIMD、および
core / imgproc の SIMD には触れていない —— PNG だけに絞った変更である。
x64 と Web には要らないので入れていない。上流が 5.1 で直せば見直す。
公開している C ABI の本数は API 対応表 の冒頭が数える(この表は
リポジトリにあり、パッケージには入らない)。
出していないものも書いておく: ステレオ校正(stereoCalibrate)、魚眼、
ステレオの平行化(stereoRectify)、視差から 3D への復元(reprojectImageTo3D)、
knnMatch / radiusMatch、FLANN ベースの照合、輪郭の階層、connectedComponents、
remap、equalizeHist、calcHist、描画関数、Haar / HOG(OpenCV 5 で contrib へ移った)、
動画入出力、GPU backend。CUDA / cuDNN は同梱しないと決めてあるが、
決め手は大きさ 1 点である —— 1 platform 分の cuDNN だけで配布上限を超えるので
結論が出てしまい、再配布条件(ライセンス)は読んでいない。
「確認して問題無かった」ではない。
この版で確かめていないこと
正直に書いておく。
ここに件数を書かない。 「完了条件が N 件閉じていない」と「確かめていないことが
N 個ある」は別の数え方で、混ぜると両方が信用できなくなる。件数の正本は
リポジトリの docs/roadmap.md の判定表である。 下は「何を確かめていないか」
の一覧であって、条件の数え上げではない。
- iOS の実機で動かしていない。 クロスビルドは CI で緑で、
.aに OpenCV が
束ねられていること(こちらの object が要求するcv::シンボルをその archive が
定義していること)も CI が毎回確かめている。しかし実機で読み込んで動かした
実績は無い —— 署名と端末が要り、CI では原理的に閉じない - Android の実機でも動かしていない。 同上
- DNN は実機で一度も動かしていない。上の 2 つがそのまま当てはまる。
推論の速さも測っていない。CI が確かめているのは、C ABI の契約(L1)、
素の .NET から実物の binary を叩けること(L3)、Unity の中で
OCVU_PROFILE_DNNを立てた assembly がコンパイルされ、IL2CPP の
stripping を生き延びることまでである。
そのレーンは Linux の 2 本だけである —— iOS / Android / Web / Windows /
macOS の Player に dnn の C# assembly が入った状態は、1 度も作られていない。
forward が返すメモリを複製している 1 行についても、それが必要だと示す
再現テストは無い(外しても壊れなかったが、確定させる経路がこのリポジトリの
構成上無い —— 復元する OpenCV の木にヘッダと lib はあるが実装ソースが無い)。
複製している側なので、返る handle が独立したメモリを持つという契約は
成立している —— 確かめていないのは「その 1 行が無くても成立するか」である - lifecycle(background / foreground)と memory pressure を検証していない
- macOS 上で Unity を起動していない。 macOS の binary と
.metaは
全部入りに入って全利用者に届くが、Unity に読ませているのは Windows と Linux 上だけ
である(.metaの解釈自体はPluginImporterに問うて確認済み) - Web は 1 つのブラウザでしか動かしていない。 CI は Linux の headless
Chromium で実際に Player を起動し、P/Invoke とメモリ転送と代表処理を通して
いる(「ビルドできた」で止めていない)。しかし他のブラウザ・実機・
モバイルのブラウザでは動かしていない RenderTextureの経路は Editor でしか動かしていない。 CI は
グラフィックス装置が在る Editor でToMat/RequestMatを実際に走らせ、
画素が正しく運ばれることを確かめている —— ただしその装置は
ソフトウェア実装である(コンテナの xvfb + Mesa)。実機の GPU ではない。しかし IL2CPP の Player では
走らせていない —— Player は-nographicsで起動され、その指定は
CI が使う道具の側にあって変えられないdnnが使う数値カーネル(MLAS / ONNX Runtime 由来)は、依存の allowlist
からは見えない。 allowlist が検査するのは成果物の中の独立したライブラリで、
libopencv_dnn.aに静的に取り込まれたものは現れない。THIRD_PARTY_NOTICESは
1 通で全 platform を代表しており、platform ごとに実際の集合は違うWITH_CAROTENEとWITH_KLEIDICVは上流の既定(ON)のままで、
ライセンスと再配布条件を確認していない。 このプロジェクトが明示的に
OFF にしている他の optional 依存(WITH_IPP/WITH_ITT)とは扱いが
違う。リンクされていること自体は実測済みで、確認していないのは条項の
ほうである
実機で確かめる手順は docs/m4-device-verification.md にある。
この版の範囲
- 対応 platform: Windows x64 / macOS arm64 / Linux x64 / Android arm64-v8a /
iOS arm64 / Web (WebGL) - Web には PNG が無い: 画像の encode / decode は JPEG のみである。
Unity の WebGL 支援が自前の libpng を同梱しているため、こちらが OpenCV の
libpng を束ねると Player のリンク段でシンボルが衝突する。束ねないほうも成立
しない(OpenCV の PNG コードが要求するシンボルが未解決になる)。どちらの
極端も通らないので、Web では PNG を外した。他の 5 platform は両方持つ。
".png"を渡すと失敗が返る。
ただし「PNG の話は Web だけ」ではない —— Android / iOS / macOS arm64 では
PNG が ARM 加速を失っている(上の「退行」) - CPU アーキテクチャ: Android は arm64-v8a のみ(x86_64 エミュレータは非対応)、
iOS は実機の arm64 のみ(シミュレータは非対応) - 公開 API:
Matのライフサイクル、cvtColor/resize/GaussianBlurと
この版で増えた画像処理(CvOps)、基本演算(CvCoreOps)、
画像の encode / decode(CvCodecs)、QR コード(CvQrCode)、
ArUco(CvAruco)、特徴点と記述子(CvFeatures)、
射影変換と姿勢(CvGeometry)、単眼カメラの校正 3 段(CvCalibration)、
ステレオの視差(CvStereo)、ONNX の推論(CvDnn、opt-in)、
Texture2D/WebCamTexture/RenderTextureの連携。
本数は API 対応表 の冒頭が数える。
API の広さではなく、所有権・stride・エラー処理・IL2CPP・platform の正しさを
固めることを優先している - encode / decode と ONNX の読み込みが扱うのはメモリ上の byte 列だけで、
ファイルパスは受けない。 ファイルを開くのは呼ぶ側の仕事である(Windows の
文字コードの扱いを境界に持ち込まないため、そして Android のStreamingAssetsは
APK の中にあってパスでは開けないため) - Unity: 6000.3 以降(2022 LTS 非対応)。検証しているのは 6000.3.16f1 の
1 版だけである - スレッド: 別々の
Matを別々のスレッドから同時に使ってよい。同じMatを
複数スレッドから同時に使うこと、使用中にDispose()することは支えない
同梱物
全部入りの UPM tarball 1 つと、その checksums.txt。 これが正である。
あわせて platform ごとに UPM tarball 1 つと、checksums / sbom / build-manifest /
THIRD_PARTY_NOTICES の 4 点。通知の先頭に、その platform の成果物に実際に
入っている component の一覧がある(本文は全 platform 共通で、一覧に無い節も含む)。
全部入りには sbom / build-manifest / THIRD_PARTY_NOTICES を付けていない。
いずれも復元済みの OpenCV の成果物(= その job の platform のもの)から作るので、
束ねる側には元が無い。統合版をでっち上げず、中身の説明は platform ごとの 4 点に任せる。
asset は全部で 33 件(6 platform × 5 + 全部入りの 2 + SHA256SUMS.txt)。
build-manifest.json には OpenCV のタグ、構成ハッシュ、generator、compiler、
ビルドしたモジュール、依存バージョン、CMake flags が実測で入っている。
dnn を足したぶん、third-party が 1 件増えた(protobuf。全 platform)。
license ファイルの数は platform ごとに違う —— Windows / Linux が 15、
macOS が 14、iOS が 13、Android が 16、Web が 13 である。Android は
cpufeatures(NDK 由来、BSD-3-Clause)の 2 件が多く clapack-lapack を欠く、
macOS と iOS は clapack-lapack を欠き iOS はさらに dlpack も欠く、
Web は libpng の 2 件を欠く(上の PNG の話と同じ理由で、そもそも入っていない)。
内訳と全文は THIRD_PARTY_NOTICES の冒頭にある。
OpenUPM
全部入りの asset 名に版番号を含めていないのは、OpenUPM の githubReleaseAssetName が
安定した接頭辞で asset を選ぶためである。登録済み
(https://package.openupm.com/com.ayutaz.opencv-unity-native)。
ただし Release のほうが先に出る。 OpenUPM は自前のビルドキューを持つので、
新しい版がそこに現れるまで数時間かかる(前の版では約 4 時間だった)。
それまで openupm add で入るのは 1 つ前の版であり、この本文が説明して
いる物ではない。 しかもエラーにはならないので、package.json を見に行かない
限り気づかない —— この版は前の版との差が大きい(C ABI が 27 本から 57 本、
CvDnn / CvAruco / CvCoreOps / CvStereo / RenderTextureConverter が
まるごと無い)ので、「ここに書いてある CvDnn が見当たらない」という形で踏む。
出したばかりの版が要るなら、上の tarball を使うこと。