簡體   English   中英

OpenCV:來自decomposeHomographyMat 的奇怪旋轉和平移矩陣

[英]OpenCV: Strange rotation and translation matrices from decomposeHomographyMat

我正在嘗試使用 OpenCV 庫中的 findHomography 函數來解決平面 2 平面投影問題。 作為一個玩具示例,我在 R2、P 中有一組點,在 R2、Q 中有第二組點,其中 Qx = Px+50,Qy = Py。 這意味着我已經將 x 坐標偏移了 50。現在我運行以下代碼:

Mat projectionMatrix = findHomography(Q, P);
vector<Point2f> projectedPoints(objectCoordinates.size());
perspectiveTransform(Q, projectedPoints, projectionMatrix);

這給了我 P,這很棒。 但是,現在我想要旋轉和平移矩陣 R & T,這就是我感到困惑的地方。 OpenCV 3 有一個函數叫做decomposeHomographyMat ,它返回R 和T 的最多4 個解(也返回法線,但我不存儲那些)。 我這樣運行它:

vector<Mat> Rs;
vector<Mat> Ts;
decomposeHomographyMat(projectionMatrix, cameraMatrix, Rs, Ts, noArray());

我使用的cameraMatrix是在之前的實驗中嘗試和測試的。 好的,所以我得到了我的四個結果。 看着它們,我注意到我得到了所有 R 的單位矩陣,這很好。 然而,所有的平移向量都是 [0,0,0]T,而我希望它們中至少有一個是 [-50,0,0]T。 我需要對decomposeHomographyMat的結果做些什么才能獲得預期的行為?

謝謝

事實證明我在某些方面是錯的,所以我決定重寫這個答案。

簡而言之 - 由於不正確的內在參數矩陣,您會得到奇怪的結果。

使用論文“Malis, E 和 Vargas, M,“深入理解基於視覺控制的單應性分解”(OpenCV 中的單應性分解的基礎)中的術語,透視變換由H表示,並稱為歐幾里得單應矩陣,其歸一化的結果G = K ^-1 * H * K (其中K是相機的校准矩陣)稱為單應矩陣

cv::findHomography()cv::decomposeHomographyMat()都使用Euclidean homography matrix 但為了將其分解為平移和旋轉, cv::decomposeHomographyMat()歐幾里得單應矩陣進行歸一化以獲得單應矩陣 它依賴於用戶提供的K來執行此規范化。

至於K的估計,我認為這超出了這個問題的范圍。 這個問題稱為Camera auto-calibration ,這是這篇維基文章的相關引用:

因此,三個視圖是在視圖之間具有固定固有參數的完全校准所需的最少數量。 高質量的現代成像傳感器和光學器件還可以對校准提供進一步的先驗約束,例如零偏斜(正交像素網格)和統一縱橫比(方形像素)。 整合這些先驗會將所需的最少圖像數量減少到兩個。

在零偏斜和方形像素假設下,您似乎可以從同一台相機的 2 幀圖像對應中提取K。 但是,我對這個主題不熟悉,所以不能給你更多的建議。

所以,為了檢查我的解釋是否正確,我做了一個小例子,在 2 個虛擬相機上以 3D 形式在平面上投影一些點,找到單應性,分解它,並允許將此分解與地面實況旋轉和平移向量進行比較. 這比實際輸入要好,因為這樣我們可以精確地知道K ,並且可以將其估計中的錯誤與Rt 中的錯誤解耦。 對於我已經檢查過的輸入,它能夠正確估計旋轉和平移向量,盡管由於某種原因,平移總是比地面實況小 10 倍。 也許這種分解只定義了一個比例(我現在不確定),但有趣的是,它與具有固定系數的地面實況值相關。

這是來源:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>


int main() {
  // set up a virtual camera
  float f = 100, w = 640, h = 480;

  cv::Mat1f K = (cv::Mat1f(3, 3) <<
      f, 0, w/2,
      0, f, h/2,
      0, 0,   1);

  // set transformation from 1st to 2nd camera (assume K is unchanged)
  cv::Mat1f rvecDeg = (cv::Mat1f(3, 1) << 45, 12, 66);
  cv::Mat1f t = (cv::Mat1f(3, 1) << 100, 200, 300);

  std::cout << "-------------------------------------------\n";
  std::cout << "Ground truth:\n";

  std::cout << "K = \n" << K << std::endl << std::endl;
  std::cout << "rvec = \n" << rvecDeg << std::endl << std::endl;
  std::cout << "t = \n" << t << std::endl << std::endl;

  // set up points on a plane
  std::vector<cv::Point3f> p3d{{0, 0, 10},
                               {100, 0, 10},
                               {0, 100, 10},
                               {100, 100, 10}};

  // project on both cameras
  std::vector<cv::Point2f> Q, P, S;

  cv::projectPoints(p3d,
                    cv::Mat1d::zeros(3, 1),
                    cv::Mat1d::zeros(3, 1),
                    K,
                    cv::Mat(),
                    Q);

  cv::projectPoints(p3d,
                    rvecDeg*CV_PI/180,
                    t,
                    K,
                    cv::Mat(),
                    P);

  // find homography
  cv::Mat H = cv::findHomography(Q, P);

  std::cout << "-------------------------------------------\n";
  std::cout << "Estimated H = \n" << H << std::endl << std::endl;


  // check by reprojection
  std::vector<cv::Point2f> P_(P.size());
  cv::perspectiveTransform(Q, P_, H);

  float sumError = 0;

  for (size_t i = 0; i < P.size(); i++) {
    sumError += cv::norm(P[i] - P_[i]);
  }

  std::cout << "-------------------------------------------\n";
  std::cout << "Average reprojection error = "
      << sumError/P.size() << std::endl << std::endl;


  // decompose using identity as internal parameters matrix
  std::vector<cv::Mat> Rs, Ts;
  cv::decomposeHomographyMat(H,
                             K,
                             Rs, Ts,
                             cv::noArray());

  std::cout << "-------------------------------------------\n";
  std::cout << "Estimated decomposition:\n\n";
  std::cout << "rvec = " << std::endl;
  for (auto R_ : Rs) {
    cv::Mat1d rvec;
    cv::Rodrigues(R_, rvec);
    std::cout << rvec*180/CV_PI << std::endl << std::endl;
  }

  std::cout << std::endl;

  std::cout << "t = " << std::endl;
  for (auto t_ : Ts) {
    std::cout << t_ << std::endl << std::endl;
  }

  return 0;
}

這是我機器上的輸出:

-------------------------------------------
Ground truth:
K =
[100, 0, 320;
0, 100, 240;
0, 0, 1]

rvec =
[45;
12;
66]

t =
[100;
200;
300]

-------------------------------------------
Estimated H =
[0.04136041220427821, 0.04748763375951008, 358.5557917287962;
0.05074854454707714, 0.06137211243830468, 297.4585754092336;
8.294458769850147e-05, 0.0002294875562580223, 1]

-------------------------------------------
Average reprojection error = 0

-------------------------------------------
Estimated decomposition:

rvec =
[-73.21470385654712;
56.64668212487194;
82.09114210289061]

[-73.21470385654712;
56.64668212487194;
82.09114210289061]

[45.00005330430893;
12.00000697952995;
65.99998380038915]

[45.00005330430893;
12.00000697952995;
65.99998380038915]


t =
[10.76993852870029;
18.60689642878277;
30.62344129378435]

[-10.76993852870029;
-18.60689642878277;
-30.62344129378435]

[10.00001378255982;
20.00002581449634;
30.0000336510648]

[-10.00001378255982;
-20.00002581449634;
-30.0000336510648]

如您所見,假設中存在正確的旋轉向量估計,並且存在符合比例的平移正確估計。

您應該在每個平面上使用solvePnP ,然后從兩個相機矩陣中獲取相對平移和旋轉(假設您至少有 4 個點)。 decomposeHomographyMat的問題是你最多可以得到4個解……你可以刪除兩個會落在圖像FOV之外的,但這仍然是一個問題。

  • @alexisrozhkov-我在您的代碼中發現了錯誤-文章和函數都假定 Z 平面為 1(而不是像您那樣的z=10 )。 下面添加的是對上述代碼的正確重新植入(已修復並在 python 中):
import cv2
import numpy as np


# set up a virtual camera
f = 100
w = 640
h = 480

K = np.array([[f, 0, w/2],
              [0, f, h/2],
              [0, 0,   1]])
dist_coffs = np.zeros(5)
# set transformation from 1st to 2nd camera (assume K is unchanged)
rvecDeg = np.array([[45, 12, 66]]).T
t = np.array([[100.0, 200, 300]]).T

print("-------------------------------------------\n")
print("Ground truth:\n")

print("K = \n" + str(K))
print("rvec = \n" + str(rvecDeg))
print("t = \n" + str(t))

# set up points on a plane
p3d = np.array([[0, 0, 1],
                [100, 0, 1],
                [0, 100, 1],
                [100, 100, 1]], dtype=np.float64)

# project on both cameras

Q, _ = cv2.projectPoints(p3d,
                         np.zeros((3, 1)),
                         np.zeros((3, 1)),
                         K,
                         dist_coffs)

P, _ = cv2.projectPoints(p3d,
                         rvecDeg*np.pi/180,
                         t,
                         K,
                         dist_coffs)

# find homography
H, _ = cv2.findHomography(Q, P)

print("-------------------------------------------\n")
print("Estimated H = \n" + str(H))


# check by reprojection
P_ = cv2.perspectiveTransform(Q, H)

sumError = 0

for i in range(P.shape[0]):
    sumError += np.linalg.norm(P[i] - P_[i])


print("-------------------------------------------\n")
print("Average reprojection error = "+str(sumError/P.shape[0]))


# decompose using identity as internal parameters matrix
num_res, Rs, ts, n = cv2.decomposeHomographyMat(H, K)

print("-------------------------------------------\n")
print("Estimated decomposition:\n\n")
for i, Rt in enumerate(zip(Rs, ts)):
    R, t = Rt
    print("option " + str(i+1))
    print("rvec = ")
    rvec, _ = cv2.Rodrigues(R)
    print(rvec*180/np.pi)
    print("t = ")
    print(t)

這就是結果(選項 3 是正確的結果):

-------------------------------------------

Ground truth:

K = 
[[100.   0. 320.]
 [  0. 100. 240.]
 [  0.   0.   1.]]
rvec = 
[[45]
 [12]
 [66]]
t = 
[[100.]
 [200.]
 [300.]]
-------------------------------------------

Estimated H = 
[[3.93678513e-03 4.51998690e-03 3.53830416e+02]
 [4.83037187e-03 5.84154353e-03 3.05790229e+02]
 [7.89487379e-06 2.18431675e-05 1.00000000e+00]]
-------------------------------------------

Average reprojection error = 1.1324020050061362e-05
-------------------------------------------

Estimated decomposition:


option 1
rvec = 
[[-78.28555877]
 [ 58.01301837]
 [ 81.41634175]]
t = 
[[100.79816988]
 [198.59277542]
 [300.6675498 ]]
option 2
rvec = 
[[-78.28555877]
 [ 58.01301837]
 [ 81.41634175]]
t = 
[[-100.79816988]
 [-198.59277542]
 [-300.6675498 ]]
option 3
rvec = 
[[45.0000205 ]
 [12.00005488]
 [65.99999505]]
t = 
[[100.00010826]
 [200.00026791]
 [300.00034698]]
option 4
rvec = 
[[45.0000205 ]
 [12.00005488]
 [65.99999505]]
t = 
[[-100.00010826]
 [-200.00026791]
 [-300.00034698]]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM