web-dev-qa-db-ja.com

OpenCVでStereoBMを使用した視差マップの不良

ステレオカムリグを組み立てましたが、それを使用して良好な視差マップを作成するのに問題があります。これは、2つの修正された画像と、それらを使用して作成した視差マップの例です。

Rectified images with disparity map

ご覧のとおり、結果はかなり悪いです。 StereoBMの設定を変更してもあまり変わりません。

セットアップ

  • 両方のカメラは同じモデルであり、USBで私のコンピューターに接続します。
  • それらは動かないように堅い木の板に固定されています。私はできる限りそれらを揃えましたが、もちろんそれは完璧ではありません。それらは移動できないため、キャリブレーション中とキャリブレーション後の位置は同じです。
  • OpenCVを使用してステレオペアをキャリブレーションし、OpenCVのStereoBMクラスを使用して視差マップを作成しています。
  • おそらくそれほど関係はありませんが、私はPythonでコーディングしています。

私が想像できる問題

私はこれを初めて行うので、専門家にはほど遠いですが、問題は視差マップの計算ではなく、キャリブレーションまたはステレオ補正にあると思います。 StereoBMの設定のすべての順列を試しましたが、結果は異なりますが、すべて上記の視差マップのようになっています。白黒のパッチです。

この考えは、私が理解しているように、ステレオ整流は各画像のすべてのポイントを整列させて、直線(私の場合は水平)で接続する必要があるという事実によってさらにサポートされます。両方の修正された写真を並べて調べると、そうではないことがすぐにわかります。対応するポイントは、左側よりも右側の画像の方がはるかに高くなっています。ただし、キャリブレーションと修正のどちらが問題なのかはわかりません。

コード

実際のコードはオブジェクトにラップされています-全体を確認したい場合は、 GitHubで を利用できます。これは実際に実行されるものの簡単な例です(もちろん、実際のコードでは、2枚以上の写真を使用して調整します)。

import cv2
import numpy as np

## Load test images
# TEST_IMAGES is a list of paths to test images
input_l, input_r = [cv2.imread(image, cv2.CV_LOAD_IMAGE_GRAYSCALE)
                    for image in TEST_IMAGES]
image_size = input_l.shape[:2]

## Retrieve chessboard corners
# CHESSBOARD_ROWS and CHESSBOARD_COLUMNS are the number of inside rows and
# columns in the chessboard used for calibration
pattern_size = CHESSBOARD_ROWS, CHESSBOARD_COLUMNS
object_points = np.zeros((np.prod(pattern_size), 3), np.float32)
object_points[:, :2] = np.indices(pattern_size).T.reshape(-1, 2)
# SQUARE_SIZE is the size of the chessboard squares in cm
object_points *= SQUARE_SIZE
image_points = {}
ret, corners_l = cv2.findChessboardCorners(input_l, pattern_size, True)
cv2.cornerSubPix(input_l, corners_l,
                 (11, 11), (-1, -1),
                 (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS,
                  30, 0.01))
image_points["left"] = corners_l.reshape(-1, 2)
ret, corners_r = cv2.findChessboardCorners(input_r, pattern_size, True)
cv2.cornerSubPix(input_r, corners_r,
                 (11, 11), (-1, -1),
                 (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS,
                  30, 0.01))
image_points["right"] = corners_r.reshape(-1, 2)

## Calibrate cameras
(cam_mats, dist_coefs, rect_trans, proj_mats, valid_boxes,
 undistortion_maps, rectification_maps) = {}, {}, {}, {}, {}, {}, {}
criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS,
            100, 1e-5)
flags = (cv2.CALIB_FIX_ASPECT_RATIO + cv2.CALIB_ZERO_TANGENT_DIST +
         cv2.CALIB_SAME_FOCAL_LENGTH)
(ret, cam_mats["left"], dist_coefs["left"], cam_mats["right"],
 dist_coefs["right"], rot_mat, trans_vec, e_mat,
 f_mat) = cv2.stereoCalibrate(object_points,
                              image_points["left"], image_points["right"],
                              image_size, criteria=criteria, flags=flags)
(rect_trans["left"], rect_trans["right"],
 proj_mats["left"], proj_mats["right"],
 disp_to_depth_mat, valid_boxes["left"],
 valid_boxes["right"]) = cv2.stereoRectify(cam_mats["left"],
                                           dist_coefs["left"],
                                           cam_mats["right"],
                                           dist_coefs["right"],
                                           image_size,
                                           rot_mat, trans_vec, flags=0)
for side in ("left", "right"):
    (undistortion_maps[side],
     rectification_maps[side]) = cv2.initUndistortRectifyMap(cam_mats[side],
                                                           dist_coefs[side],
                                                           rect_trans[side],
                                                           proj_mats[side],
                                                           image_size,
                                                           cv2.CV_32FC1)

## Produce disparity map
rectified_l = cv2.remap(input_l, undistortion_maps["left"],
                        rectification_maps["left"],
                        cv2.INTER_NEAREST)
rectified_r = cv2.remap(input_r, undistortion_maps["right"],
                        rectification_maps["right"],
                        cv2.INTER_NEAREST)
cv2.imshow("left", rectified_l)
cv2.imshow("right", rectified_r)
block_matcher = cv2.StereoBM(cv2.STEREO_BM_BASIC_PRESET, 0, 5)
disp = block_matcher.compute(rectified_l, rectified_r, disptype=cv2.CV_32F)
cv2.imshow("disparity", disp)

ここで何が問題になっていますか?

14
Daniel Lee

問題は視覚化であり、データ自体ではないことが判明しました。どこかで、cv2.reprojectImageTo3Dが浮動小数点値として視差マップを必要としていることを読みました。そのため、cv2.CV_32Fからblock_matcher.computeを要求していました。

OpenCVのドキュメントを注意深く読むと、これは間違っていると思っていたので、速度を上げるために実際には浮動小数点数ではなく整数を使用したいと思いましたが、cv2.imshowのドキュメントは明確ではありませんでした。 (16ビットの符号なし整数と比較して)16ビットの符号付き整数で何が行われるかについては、視覚化のために値を浮動小数点数のままにしておきます。

cv2.imshow のドキュメントは、32ビット浮動小数点値が0から1の間であると想定されているため、255を掛けることを示しています。255は、ピクセルが表示される飽和点です。白として。私の場合、この仮定によりバイナリマップが作成されました。 OpenCVでも同じことを行うという事実をキャンセルするために、手動で0〜255の範囲にスケーリングしてから255で除算しました。恐ろしい操作ですが、StereoBMをオフラインに調整するためだけに行っているので、パフォーマンスは重要ではありません。解決策は次のようになります。

# Other code as above
disp = block_matcher.compute(rectified_l, rectified_r, disptype=cv2.CV_32F)
norm_coeff = 255 / disp.max()
cv2.imshow("disparity", disp * norm_coeff / 255)

次に、視差マップは問題ないように見えます。

17
Daniel Lee