我发现我的某些观点是错误的,因此我决定重写这个答案。
简而言之 - 您会得到奇怪的结果,是因为内在参数矩阵不正确。
使用论文“Malis、E和Vargas、M,”基于视觉控制的单应性分解的深入理解“(OpenCV中的单应性分解基于该论文的术语),透视变换用H表示,并称为欧几里得单应性矩阵,其归一化结果G = K^-1 * H * K(其中K是相机的校准矩阵)称为单应性矩阵
cv :: findHomography()
和cv :: decomposeHomographyMat()
都使用欧几里得单应性矩阵。 但是,为了将其分解为平移和旋转,cv :: decomposeHomographyMat()
将欧几里得单应性矩阵归一化以获得单应性矩阵。 它依靠用户提供的K来执行这种归一化。
关于估计
K的问题,我认为这超出了这个问题的范围。这个问题被称为
相机自动标定,以下是来自维基百科文章的相关引用:
因此,至少需要三个视角进行完整校准,视角之间的内部参数固定。优质的现代成像传感器和光学器件还可以提供关于校准的更多先验约束条件,如零偏移(正交像素网格)和单位纵横比(正方形像素)。集成这些先验条件将减少所需图像的最小数量至两张。
看起来,您可以在零偏移和正方形像素的假设下,从同一台相机的两帧图像对中提取
K。但是,我对这个主题不熟悉,无法给出更多建议。
因此,为了检查我的理解是否正确,我制作了一个小例子,在其中将一些点在3D平面上投影到2个虚拟相机上,找到单应性矩阵并对其进行分解,然后将此分解与真实的旋转和平移向量进行比较。这比使用真实输入更好,因为这样我们可以精确地知道
K,并且可以将其估计误差与
R和
t的误差分离开来。对于我检查过的输入,它能够正确地估计旋转和平移向量,尽管由于某种原因平移向量始终比真实值小10倍。也许这个分解只定义了一个比例(现在我不确定),但有趣的是,它与真实值之间的关系是由一个固定系数确定的。
以下是源代码:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
int main() {
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);
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;
std::vector<cv::Point3f> p3d{{0, 0, 10},
{100, 0, 10},
{0, 100, 10},
{100, 100, 10}};
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);
cv::Mat H = cv::findHomography(Q, P);
std::cout << "-------------------------------------------\n";
std::cout << "Estimated H = \n" << H << std::endl << std::endl;
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;
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]
正如您所见,假设中存在旋转向量的正确估计,并且存在比例正确的平移估计。