このブログは、株式会社フィックスターズのエンジニアが、あらゆるテーマについて自由に書いているブログです。
初めまして、アルバイトの大塚です。adaskitという社内の自動運転関連のオープンソースプロジェクトに微力ながら携わっており、弊社が公開している視差計算ライブラリlibSGMの開発を行っていました。
libSGMが機能追加などで成熟してきたことを受け、adaskitチーム内でOpenCVに取り込んでもらうことができないか検討するようになり、調査と移植作業の末にPull Requestを送りました。
その結果Pull Requestが承認され、version 4.5.1にて利用可能になりました。
OpenCVでlibSGMが利用できるようになったことを確認してみましょう。opencv/opencv、opencv/opencv_contribから4系の最新版を入手し、ビルド・インストールします。libSGMはcudastereo moduleに追加されているため、BUILD_opencv_cudastereo
をONにします。
OpenCVはPython bindingsを備えており、開発者は関数などにマクロを追加するだけでPythonから利用できるようになります。今回は簡単な動作確認としてPythonスクリプトを用意しました。
OpenCVのperfテストで利用されているaloeの画像を入力に与えてみます。
期待した結果が得られました。libSGMの開発に携わった一個人としては、OpenCVの機能にlibSGM(の移植)が入り、動かせることに感動します。
速度は移植前のlibSGMと比べてどうでしょうか。libSGMのbenchmark sampleを用いて計測しました。計測条件と結果は以下の通りです。
device name | GeForce GTX 750 Ti |
CUDA runtime version | 10020 |
image size | [1024 x 440] |
disparity size | 128 |
output depth | 16 |
subpixel option | true |
sgm path | 8 path |
iterations | 1000 |
cv::cuda::StereoSGM | sgm::StereoSGM (original) | |
Processing Time [ms] | 20.5 | 19.0 |
FPS | 48.9 | 52.7 |
移植前と遜色ないパフォーマンスを発揮できています。若干の差は移植するにあたり、既存のクラス・構造体を用いたことに依るものではないかと考えています。
ここでは今回苦労した・体験した点を書いていこうと思います。
OpenCVは開発者向けに以下のドキュメントを用意しています。
これらを読む他、既存のPull Requestのレビューでどのような点が指摘されているか確認しました。
以上を元にlibSGMをOpenCVスタイルに移植し、動作確認しました。
CUDAでステレオマッチングを行う、という機能に合ったmoduleがopencv_contribのcudastereoに当たるため、こちらで作業しました。
移植作業当時、OpenCVのCMakeビルド設定CUDA_ARCH_BIN
に2.0が含まれ、Warp Shuffle関数がないとエラーが発生しました。libSGMではCompute Capability 3.5以上をサポートしていますが、OpenCVに取り入れてもらうため共有メモリを使った実装をラッパーに追加し、テストしました。
// https://github.com/opencv/opencv_contrib/blob/4.5.1/modules/cudastereo/src/cuda/stereosgm.cu#L100-L119
template <unsigned int WARPS_PER_BLOCK, typename T>
__device__ __forceinline__ static T shfl_xor(T var, int laneMask, int width = cudev::WARP_SIZE, uint32_t mask = 0xFFFFFFFFU)
{
#if __CUDA_ARCH__ >= 300
#if CUDA_VERSION >= 9000
return __shfl_xor_sync(mask, var, laneMask, width);
#else
return __shfl_xor(var, laneMask, width);
#endif // CUDA_VERSION
#else
static __shared__ T smem[WARPS_PER_BLOCK][cudev::WARP_SIZE];
smem[cudev::Warp::warpId()][cudev::Warp::laneId()] = var;
T ret = var;
if (((cudev::Warp::laneId() % width) ^ laneMask) < width)
{
ret = smem[cudev::Warp::warpId()][cudev::Warp::laneId() ^ laneMask];
}
return ret;
#endif // __CUDA_ARCH__
}
この他、同様の理由で__ldg
関数のラッパーを作成しました。
OpenCVに新しい機能を追加する場合、テストが必要です。libSGMには既存のテストがありますが、これに加えてOpenCVのStereoSGBMで用いられているテストを追加しました。
このテストの仕組みは簡単で、まずパラメータファイルを用意してテストを実行するとGround Truthとの比較結果が保存されます。次回実行時にはその比較結果と相違ないか確認が行わる、というものです。この比較結果はopencv/opencv_extraをForkしたリポジトリにcommitしておき、opencv_contribと同時にPull Requestを送ります。
終盤近くになってCIを回すようにしました。CI用のイメージはopencv-infrastructure /opencv-worker-configリポジトリにあるDockerfileを参考にしました。
文面を整え、緊張の中いざPull Requestを作成した後は以下のように事が進みました。
force_builders=Custom
buildworker:Custom=linux-4
build_image:Custom=ubuntu-cuda:16.04
問題なくmergeされ、とても安心しました。
libSGMがOpenCVに移植されたことで、より多くの方に触れていただける機会が増えることを願っています。
アルバイトでは日々貴重な体験をさせていただいていますが、「OpenCVに大きなサイズのPull Requestを送る」という経験ができてよかったです。
事前のレビューや相談に対応して下さった高木章洋さん、OpenCVに関する知識を提供して下さった吉村康弘さん、Pull Requestに対応して下さったalalekさんにこの場を借りて感謝申し上げます。
なお、今回のPull Requestでは移植作業全般を担当したことを理由に、commitのauthorを大塚とさせていただきました。
libSGMの実装が含まれるcudastereo/src/cuda/stereosgm.cuにはauthorをadaskit Teamと記しております。
コンピュータビジョンセミナーvol.2 開催のお知らせ - ニュース一覧 - 株式会社フィックスターズ in Realizing Self-Driving Cars with General-Purpose Processors 日本語版
[…] バージョンアップに伴い、オンラインセミナーを開催します。 本セミナーでは、...
【Docker】NVIDIA SDK Managerでエラー無く環境構築する【Jetson】 | マサキノート in NVIDIA SDK Manager on Dockerで快適なJetsonライフ
[…] 参考:https://proc-cpuinfo.fixstars.com/2019/06/nvidia-sdk-manager-on-docker/ […]...
Windowsカーネルドライバを自作してWinDbgで解析してみる① - かえるのほんだな in Windowsデバイスドライバの基本動作を確認する (1)
[…] 参考:Windowsデバイスドライバの基本動作を確認する (1) - Fixstars Tech Blog /proc/cpuinfo ...
2021年版G検定チートシート | エビワークス in ニューラルネットの共通フォーマット対決! NNEF vs ONNX
[…] ONNX(オニキス):Open Neural Network Exchange formatフレームワーク間のモデル変換ツー...
YOSHIFUJI Naoki in CUDAデバイスメモリもスマートポインタで管理したい
ありがとうございます。別に型にこだわる必要がないので、ユニバーサル参照を受けるよ...