繁体   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