OpenCV

OpenCVは事実上の標準となっているオープンソースのコンピュータビジョンライブラリであり、SLAMの作業においては日常的に手を伸ばすツールボックスである。C++で書かれており、ほぼ完全なPythonバインディング(pip install opencv-python)を持つため、同じAPIが高速なプロトタイピングと本番向けフロントエンドの両方に使える。

SLAMにとって最も重要なOpenCVの部分は次のとおりである。

Pythonにおける最小の特徴マッチングパイプライン:

import cv2

orb = cv2.ORB_create(2000)
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)

matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = sorted(matcher.match(des1, des2), key=lambda m: m.distance)

pts1 = cv2.KeyPoint_convert(kp1, [m.queryIdx for m in matches])
pts2 = cv2.KeyPoint_convert(kp2, [m.trainIdx for m in matches])
E, inliers = cv2.findEssentialMat(pts1, pts2, K, method=cv2.RANSAC)
_, R, t, _ = cv2.recoverPose(E, pts1, pts2, K, mask=inliers)

パイプラインを続けると、3D点の地図を手に入れた後は、新しいフレームをトラッキングすることはPnP問題になる——このパターン(投影、マッチング、solvePnPRansac、リファインメント)は、基本的に特徴ベースSLAMのトラッキングスレッドが行っていることそのものである。

# pts3d: Nx3 map points, pts2d: Nx2 matched pixel observations
ok, rvec, tvec, inliers = cv2.solvePnPRansac(
    pts3d, pts2d, K, distCoeffs,
    reprojectionError=2.0, iterationsCount=100)
R, _ = cv2.Rodrigues(rvec)          # world-to-camera rotation
# triangulate new points from two calibrated views
P1 = K @ np.hstack([np.eye(3), np.zeros((3, 1))])
P2 = K @ np.hstack([R, tvec])
X_h = cv2.triangulatePoints(P1, P2, pts1.T, pts2.T)
X = (X_h[:3] / X_h[3]).T            # homogeneous -> Euclidean

その限界も認識しておくべきである。OpenCVはフロントエンドの構成要素を提供するが、SLAMバックエンドは提供しない。バンドル調整、ポーズグラフ、ファクターグラフはCeres、g2o、GTSAMに存在する。OpenCVの役割は、画像を入力として、対応関係と初期姿勢を出力することである。

よくある落とし穴

SLAMにおける意義

ほぼすべてのオープンソースSLAMシステム——ORB-SLAM、VINS-Mono、その他数多くの研究プロトタイプ——が画像処理、特徴抽出、幾何ソルバーにOpenCVを使っている。これに習熟しているということは、それらのコードベースを読むことができ、完全な視覚オドメトリパイプライン(検出、マッチング、RANSAC、PnP)を一晩で構築できるということであり、これはレベル2で推奨される演習である。

関連ノート