はじめに
.NET Coreがクロスプラットフォーム対応になって、
LinuxだろうがMacだろうが、x86だろうがarmhfだろうが、
動くようになったこのご時世だというのに、全然試していなかったので試しました。
.NET Core の導入
まずは .NET Core の導入です。
ARMでは ARM32 と ARM64 のバージョンがあるので、ターゲットに合わせて選びます。
今回はTinkerBoardというRaspberry Pi みたいな armhf (armv7l)なシングルボードコンピュータに導入しようと思いますので、
「SDK 3.1.402
」の「ARM32
」を選びました。
OpenCV 4.4.0 の導入
関係ないですが、OpenCV 4.4.0 では晴れて自由に使えるようになった SIFT特徴量
がメインのモジュールに含まれるようになりました。(opencv_features2d
に入ってます。)
※今回はソースコードからビルドしてみますが、他のバージョンで良ければいろんなところで配布されているのでそれを使用した方が早いです。
OpenCV 4.4.0 自体のビルド
いつもと同じように、CMakeを使って構成、Makeでビルド/インストールします。
OpenCV本体のダウンロード
mkdir src
cd src
wget https://github.com/opencv/opencv/archive/4.4.0.zip
unzip 4.4.0.zip
rm 4.4.0.zip
OpenCV contribのダウンロード
wget https://github.com/opencv/opencv_contrib/archive/4.4.0.zip
unzip 4.4.0.zip
rm 4.4.0.zip
ダウンロードし展開すると、このように opencv-4.4.0
と opencv_contrib-4.4.0
フォルダができあがります。(両方 4.4.0.zip
というzipファイルなのでダウンロード時に上書きしないように注意。)
~/src$ ls
4.4.0.zip opencv-4.4.0 opencv_contrib-4.4.0
そしてCMakeを使ってビルドする準備をします。
cd opencv-4.4.0
mkdir build
cd build
cmake -DOPENCV_EXTRA_MODULES_PATH=/home/XXX/src/opencv_contrib-4.4.0/modules -D WITH_LIBV4L=ON -D CMAKE_BUILD_TYPE=RELEASE -D WITH_TBB=ON -D ENABLE_NEON=ON ..
-DOPENCV_EXTRA_MODULES_PATH
は 先程の opencv_contrib-4.4.0
フォルダ配下の modules
を指定します。(オプションは一例です。この例ではあんまり真面目に考えてませんが、Python用のモジュールをビルドしないなど最適化する余地はあります。)
次に、ビルド/インストールをします。
make
sudo make install
sudo ldconfig
さすがに実機でやると時間がめっちゃかかるので screen
コマンドで裏で走らせつつ寝ました。
OpenCvSharpのビルド
さて、ここまでで opencv の導入はできたので、それを .NET Core から使うべくOpenCVをC#ラッパーである OpenCvSharp
を導入していきます。
shimat/opencvsharp: OpenCV wrapper for .NET
OpenCvSharpには OpenCV を軽くラップした薄いラッパーが同梱している(OpenCvSharp4.runtime.win
とかが多分それ。)のですが、Ubuntu用は x64用しか無いのでARM版を手動でビルドしていきます。
ビルド手順はまんまこれ → Build on ARM · Issue #388 · shimat/opencvsharp
# download OpenCvSharp
cd ~/src
git clone https://github.com/shimat/opencvsharp.git
# install the Extern lib.
cd opencvsharp/src
sed -i.bak '5i\
include_directories("/usr/local/include/")\
set (CMAKE_CXX_STANDARD 11)\
' CMakeLists.txt
mkdir build
cd build
cmake ..
make
sudo make install
sudo ldconfig
sudo ldconfig
までやると NuGet で OpenCvSharp落としてくるだけで使えるようになる。めっちゃすごい。
クロスプラットフォームサンプル
というわけで、上記手順に従うと .NET Core + OpenCV をやる最低限の準備は整うことがわかりました。ただ、やっぱりサンプルがないと寂しいので書いてみました。
一応 Windows 10 on ThinkPad X220 (x64) と Debian 9 on TinkerBoard (armv7l) で動作確認を取ったサンプルです。
このサンプルはカレントディレクトリの example.jpg というファイルを読み込んでキャニーのエッジ抽出をして結果を example_canny.jpg として保存するものです。
static void Main(string[] args) {
using var src = new Mat("example.jpg", ImreadModes.Grayscale);
using var dst = new Mat();
Cv2.Canny(src, dst, 50, 200);
Cv2.ImWrite("example_canny.jpg", dst);
Console.WriteLine("Example done!");
}
サンプルのキモ
極力共通のコマンドを叩くと環境が揃ってビルドできるようになるようにしたかったので csproj
ファイルの中でプラットフォーム依存の部分を記述しています。
<ItemGroup Condition="$(OS.StartsWith('Windows'))">
<PackageReference Include="OpenCVSharp4.Windows" Version="4.4.0.20200915" />
</ItemGroup>
といってもこれだけですが。
ここでやっているのは、Windowsでビルドしたときは OpenCvSharp の「All-in-one package」への参照をもたせて先程のようなOpenCVをWindowsでビルドする必要をなくする処理です。先程紹介したOpenCVのビルド手順をなぞればここの記述は必要ありませんが、正直気力が足りないのと素直にビルド済みの配布物を使用するほうが後々トラブルが少ないという考えからです。
TinkerBoardでビルドしてみる
先程のリポジトリをTinkerBoardにクローンしてビルドしてみます。
yoh@tinkerboard:~/dev$ git clone https://github.com/yoh1496/dotnetcore-opencvsharp
Cloning into 'dotnetcore-opencvsharp'...
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 12 (delta 1), reused 11 (delta 0), pack-reused 0
Unpacking objects: 100% (12/12), done.
yoh@tinkerboard:~/dev$ cd dotnetcore-opencvsharp/
yoh@tinkerboard:~/dev/dotnetcore-opencvsharp$ dotnet build .
Microsoft (R) Build Engine version 16.7.0+7fb82e5b2 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.
Determining projects to restore...
Restored /home/yoh/dev/dotnetcore-opencvsharp/dotnetcore-opencvsharp.csproj (in 960 ms).
dotnetcore-opencvsharp -> /home/yoh/dev/dotnetcore-opencvsharp/bin/Debug/netcoreapp3.1/dotnetcore-opencvsharp.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:08.28
ビルドできますね。最高。
ここで、入力画像を dotnetcore-opencvsharp
フォルダ直下に配置します。scp
で渡しました。
画像は何でもいいので適当に旅行で撮った写真を使ってみました。(掲載に当たり縮小してます)
yoh@tinkerboard:~/dev/dotnetcore-opencvsharp$ ls
Program.cs bin dotnetcore-opencvsharp.csproj example.jpg obj
では実行!
yoh@tinkerboard:~/dev/dotnetcore-opencvsharp$ dotnet run .
Example done!
yoh@tinkerboard:~/dev/dotnetcore-opencvsharp$ ls
Program.cs bin dotnetcore-opencvsharp.csproj example.jpg example_canny.jpg obj
example_canny.jpg
が出力されてる!
出力結果を確認してみます。 scp
で取ってきてもいいんですが、面倒くさかったので Visual Studio Code の Remote-SSH 拡張機能で開きました。
ちゃんとエッジ抽出できてる!
Windows で実行してみる
できました!
SSH-Remote で armhf の Linux につなぐとデバッガが使用できないんですが、Windows環境だとちゃんと使用できて便利です。開発はWindowsでやって、実際に動かすのはARMみたいな使い分けをしていこうかなと思います。
ARM でのみ発生する問題もあると思うので、そういった場合はリモートデバッグを使うとよいかもしれません。まだ試せてませんが、、、
Remote Debugging On Linux Arm · OmniSharp/omnisharp-vscode Wiki
終わりに
Windowsで書いたコードがそのまま Linux on ARM でも動作するのは便利ですね。試してませんがまぁMacでも動くんでしょう。
.NET Coreが動くのが便利なのもそうなんですが、vscodeがARMのLinuxマシン上でも問題なく繋げるのが最高に便利ですね。