5

OpenCV を使用して、SIFT 機能追跡、FLANN マッチング、および基本行列と必須行列のその後の計算を使用して、カメラの 1 つのポーズを別のカメラと比較して推定しようとしています。本質的な行列を分解した後、縮退構成をチェックし、「正しい」R と t を取得します。

問題は、それらが決して正しくないように見えることです。いくつかの画像ペアを含めています。

  1. Y 軸に沿って 45 度回転し、画像 1 と同じ位置で撮影された画像 2。

画像ペア 画像ペア1

結果

結果1

  1. 約から撮影した画像2。負の X 方向に沿って数メートル離れ、負の Y 方向にわずかに変位します。約。Y 軸に沿ったカメラ ポーズの 45 ~ 60 度の回転。

画像ペア 画像ペア 2

結果

結果2

2 番目のケースの並進ベクトルは、Y の動きを過大評価し、X の動きを過小評価しているようです。回転行列をオイラー角に変換すると、両方のケースで間違った結果が得られます。これは、他の多くのデータセットでも発生します。基本的な行列計算手法を RANSAC、LMEDS などの間で切り替えてみましたが、現在は RANSAC と 8 点法でインライアのみを使用して 2 番目の計算を行っています。特徴検出方法を変更しても、どちらも役に立ちません。エピポーラ線は適切なようで、基本行列は x'.Fx = 0 を満たします。

ここで根本的に間違っているものを見逃していますか? プログラムがエピポーラ ジオメトリを適切に理解しているとすると、完全に間違ったポーズになる原因は何でしょうか? ポイントが両方のカメラの前にあることを確認するためのチェックを行っています。どんな考えや提案も非常に役に立ちます。ありがとう!

編集: 間隔をあけて配置された 2 つの異なる校正済みカメラで同じ手法を試しました。必須行列を K2'.F.K1 として計算しましたが、それでも平行移動と回転はまだかなりずれています。

参考コード

import cv2
import numpy as np

from matplotlib import pyplot as plt

# K2 = np.float32([[1357.3, 0, 441.413], [0, 1355.9, 259.393], [0, 0, 1]]).reshape(3,3)
# K1 = np.float32([[1345.8, 0, 394.9141], [0, 1342.9, 291.6181], [0, 0, 1]]).reshape(3,3)

# K1_inv = np.linalg.inv(K1)
# K2_inv = np.linalg.inv(K2)

K = np.float32([3541.5, 0, 2088.8, 0, 3546.9, 1161.4, 0, 0, 1]).reshape(3,3)
K_inv = np.linalg.inv(K)

def in_front_of_both_cameras(first_points, second_points, rot, trans):
    # check if the point correspondences are in front of both images
    rot_inv = rot
    for first, second in zip(first_points, second_points):
        first_z = np.dot(rot[0, :] - second[0]*rot[2, :], trans) / np.dot(rot[0, :] - second[0]*rot[2, :], second)
        first_3d_point = np.array([first[0] * first_z, second[0] * first_z, first_z])
        second_3d_point = np.dot(rot.T, first_3d_point) - np.dot(rot.T, trans)

        if first_3d_point[2] < 0 or second_3d_point[2] < 0:
            return False

    return True

def drawlines(img1,img2,lines,pts1,pts2):
    ''' img1 - image on which we draw the epilines for the points in img1
        lines - corresponding epilines '''
    pts1 = np.int32(pts1)
    pts2 = np.int32(pts2)
    r,c = img1.shape
    img1 = cv2.cvtColor(img1,cv2.COLOR_GRAY2BGR)
    img2 = cv2.cvtColor(img2,cv2.COLOR_GRAY2BGR)
    for r,pt1,pt2 in zip(lines,pts1,pts2):
        color = tuple(np.random.randint(0,255,3).tolist())
        x0,y0 = map(int, [0, -r[2]/r[1] ])
        x1,y1 = map(int, [c, -(r[2]+r[0]*c)/r[1] ])
        cv2.line(img1, (x0,y0), (x1,y1), color,1)
        cv2.circle(img1,tuple(pt1), 10, color, -1)
        cv2.circle(img2,tuple(pt2), 10,color,-1)
    return img1,img2


img1 = cv2.imread('C:\\Users\\Sai\\Desktop\\room1.jpg', 0)  
img2 = cv2.imread('C:\\Users\\Sai\\Desktop\\room0.jpg', 0) 
img1 = cv2.resize(img1, (0,0), fx=0.5, fy=0.5)
img2 = cv2.resize(img2, (0,0), fx=0.5, fy=0.5)

sift = cv2.SIFT()

# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)

# FLANN parameters
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # or pass empty dictionary

flann = cv2.FlannBasedMatcher(index_params,search_params)

matches = flann.knnMatch(des1,des2,k=2)

good = []
pts1 = []
pts2 = []

# ratio test as per Lowe's paper
for i,(m,n) in enumerate(matches):
    if m.distance < 0.7*n.distance:
        good.append(m)
        pts2.append(kp2[m.trainIdx].pt)
        pts1.append(kp1[m.queryIdx].pt)

pts2 = np.float32(pts2)
pts1 = np.float32(pts1)
F, mask = cv2.findFundamentalMat(pts1,pts2,cv2.FM_RANSAC)

# Selecting only the inliers
pts1 = pts1[mask.ravel()==1]
pts2 = pts2[mask.ravel()==1]

F, mask = cv2.findFundamentalMat(pts1,pts2,cv2.FM_8POINT)

print "Fundamental matrix is"
print 
print F

pt1 = np.array([[pts1[0][0]], [pts1[0][1]], [1]])
pt2 = np.array([[pts2[0][0], pts2[0][1], 1]])

print "Fundamental matrix error check: %f"%np.dot(np.dot(pt2,F),pt1)
print " "


# drawing lines on left image
lines1 = cv2.computeCorrespondEpilines(pts2.reshape(-1,1,2), 2,F)
lines1 = lines1.reshape(-1,3)
img5,img6 = drawlines(img1,img2,lines1,pts1,pts2)

# drawing lines on right image
lines2 = cv2.computeCorrespondEpilines(pts1.reshape(-1,1,2), 1,F)
lines2 = lines2.reshape(-1,3)
img3,img4 = drawlines(img2,img1,lines2,pts2,pts1)

E = K.T.dot(F).dot(K)

print "The essential matrix is"
print E
print 

U, S, Vt = np.linalg.svd(E)
W = np.array([0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]).reshape(3, 3)

first_inliers = []
second_inliers = []
for i in range(len(pts1)):
    # normalize and homogenize the image coordinates
    first_inliers.append(K_inv.dot([pts1[i][0], pts1[i][1], 1.0]))
    second_inliers.append(K_inv.dot([pts2[i][0], pts2[i][1], 1.0]))

# Determine the correct choice of second camera matrix
# only in one of the four configurations will all the points be in front of both cameras
# First choice: R = U * Wt * Vt, T = +u_3 (See Hartley Zisserman 9.19)

R = U.dot(W).dot(Vt)
T = U[:, 2]
if not in_front_of_both_cameras(first_inliers, second_inliers, R, T):

    # Second choice: R = U * W * Vt, T = -u_3
    T = - U[:, 2]
    if not in_front_of_both_cameras(first_inliers, second_inliers, R, T):

        # Third choice: R = U * Wt * Vt, T = u_3
        R = U.dot(W.T).dot(Vt)
        T = U[:, 2]

        if not in_front_of_both_cameras(first_inliers, second_inliers, R, T):

            # Fourth choice: R = U * Wt * Vt, T = -u_3
            T = - U[:, 2]

# Computing Euler angles

thetaX = np.arctan2(R[1][2], R[2][2])
c2 = np.sqrt((R[0][0]*R[0][0] + R[0][1]*R[0][1]))

thetaY = np.arctan2(-R[0][2], c2)

s1 = np.sin(thetaX)
c1 = np.cos(thetaX)

thetaZ = np.arctan2((s1*R[2][0] - c1*R[1][0]), (c1*R[1][1] - s1*R[2][1]))

print "Pitch: %f, Yaw: %f, Roll: %f"%(thetaX*180/3.1415, thetaY*180/3.1415, thetaZ*180/3.1415)

print "Rotation matrix:"
print R
print
print "Translation vector:"
print T

plt.subplot(121),plt.imshow(img5)
plt.subplot(122),plt.imshow(img3)
plt.show()
4

4 に答える 4

3

点の対応関係からカメラの姿勢を正確に推定できない場合が多くあります。あなたが考慮しなければならないいくつかの要因: -

(*) 8 点法は、代数誤差 ( x'.Fx = 0) を最小限に抑えます。通常は、意味のある幾何学的誤差を最小化する解を見つける方が適切です。たとえば、RANSAC 実装で再投影エラーを使用できます。

(*) 基本行列を 8 点から解く線形アルゴリズムは、ノイズに敏感です。より良い結果を得るには、サブピクセル精度のポイント マッチング、適切なデータ正規化、および正確なカメラ キャリブレーションがすべて重要です。

(*) 特徴点のローカリゼーションとマッチングは、ノイズの多い点の一致につながります。したがって、代数方程式 x'Fx を解くことによって得られる解は、最初の推定値として実際に使用する必要があり、パラメーターの最適化などのさらなる手順を適用して解を改良する必要があります。 .

(*) 一部の 2 ビュー カメラ構成はあいまいなソリューションにつながる可能性があるため、信頼できる結果を得るには、さらなる方法 (3 番目のビューの明確化など) が必要です。

于 2015-08-11T13:14:27.493 に答える
0

カメラの内部パラメータである K をどのように取得しますか? 一致点がエピポーラ線上にあるため、基本行列の計算は正しいように思えます。しかし、行列 K が不正確な場合、間違った基本行列が得られ、したがって間違った R と t が得られる可能性があります。

于 2015-08-11T06:31:28.657 に答える
0

本「Programming Computer Vision with Python」では、次のようなコードを使用して E のランクを確認する必要があります。

エッセンシャル マトリックスから 2 番目のカメラ マトリックス (P1 = [I 0] と仮定) を計算します。出力は、可能な 4 つのカメラ マトリックスのリストです。

Eがランク2であることを確認してください

U,S,V = np.linalg.svd(E) 
if np.linalg.det(np.dot(U,V))<0:
    V = -V
E = np.dot(U,np.dot(np.diag([1.0,1.0,0.0]),V))

これでもパフォーマンスが向上するかどうかはわかりません。私にお知らせください。

于 2016-04-24T21:07:24.280 に答える