このブログは、株式会社フィックスターズのエンジニアが、あらゆるテーマについて自由に書いているブログです。
初めまして、アルバイトの大塚です。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の画像を入力に与えてみます。
python3 sgm_sample.py /path/to/opencv_extra/testdata/gpu/perf/aloe{,R}.png
期待した結果が得られました。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と記しております。
keisuke.kimura in Livox Mid-360をROS1/ROS2で動かしてみた
Sorry for the delay in replying. I have done SLAM (FAST_LIO) with Livox MID360, but for various reasons I have not be...
Miya in ウエハースケールエンジン向けSimulated Annealingを複数タイルによる並列化で実装しました
作成されたプロファイラがとても良さそうです :) ぜひ詳細を書いていただきたいです!...
Deivaprakash in Livox Mid-360をROS1/ROS2で動かしてみた
Hey guys myself deiva from India currently i am working in this Livox MID360 and eager to knwo whether you have done the...
岩崎システム設計 岩崎 満 in Alveo U50で10G Ethernetを試してみる
仕事の都合で、検索を行い、御社サイトにたどりつきました。 内容は大変参考になりま...
Prabuddhi Wariyapperuma in Livox Mid-360をROS1/ROS2で動かしてみた
This issue was sorted....