解決一個問題,關於在博客:
關於opencv2.4.10-3.3.1左右版本的特徵點剔除與顯示問題
中的論文實現:
隨機抽取8個不同點,使用上圖公式(5),求取F矩陣和H矩陣
公式中左邊F那一塊最小值爲0,右邊H那一塊最小值爲0,整體公式E其實是一個優化問題
方式通過最小二乘估計求解方程組,AX=0
#include<cv.h>
#include<iostream>
using namespace std;
using namespace cv;
int main()
{
Mat A(7, 3, CV_64FC1);
Mat vec(3, 1, CV_64FC1);//最後的答案
for(int i=0;i<7;i++)
{
for(int j=0;j<3;++j)
{
A.at<double>(i,j)=i*j-i;//初始化A的值
}
}
SVD::solveZ(A,vec );
}
爲什麼H矩陣的自由度爲8:
假設兩張圖像中的對應點對齊次座標爲(x',y',1)和(x,y,1),單應矩陣H定義爲:
矩陣展開後有3個等式,將第3個等式代入前兩個等式中可得:
(下面的式子這麼寫是因爲要除以1,那個1的表示就是下面式子中的分母,爲什麼除以1,因爲齊次座標系)
也就是說,一個點對對應兩個等式。在此插入一個討論:單應矩陣H有幾個自由度?
或許有人會說,9個啊,H矩陣不是9個參數嗎?從h11到h33總共9個。真的是這樣嗎?實際上並不是,因爲這裏使用的是齊次座標系,也就是說可以進行任意尺度的縮放。比如我們把hij乘以任意一個非零常數k並不改變等式結果:
如果這裏,存在一種情況,k的值,正好等於,1/h33,僅僅是值相等
就是不管h33是什麼未知數,因爲k可以是任意非0數,所以肯定有一種尺度的縮放,使其k的值和1/h33相等
所以實際上單應矩陣H只有8個自由度。8自由度下H計算過程有兩種方法。
通過上述講解,我相信大家已經明白了,H的自由度爲8,所以,實際運算中,方便理解期間,我們直接把h33設爲1
接着,我們把F矩陣寫出來:
F矩陣的自由度爲7,和H矩陣有一點一樣,可以將f9,直接設置爲1,因爲F矩陣也是在齊次座標系下
H矩陣有兩個公式,4個匹配對,就可解決8自由度的H矩陣的問題
F矩陣只有一個公式,我們一般去解決問題時,
視F矩陣爲8自由度,不去管F矩陣還應該找到的一個自由度了,直接把f9設置爲1,8個匹配對,就可解決8自由度的F矩陣的問題
這種方法,很多博客稱爲8點法,求解F矩陣
別的博客公開的GitHub代碼:
https://blog.csdn.net/kokerf/article/details/72630863?locationNum=2&fps=1
https://github.com/kokerf/vision_kit/blob/master/module/epipolar_geometry/src/homography.cpp
https://github.com/kokerf/vision_kit/blob/master/module/epipolar_geometry/src/fundamental.cpp
Mat F_H(18, 1, CV_32F, Scalar(0));//最後的答案
std::vector<cv::Point2f> pts_prev;
std::vector<cv::Point2f> pts_next;
for (size_t H_F_i = 0; H_F_i < 8; H_F_i++)
{
pts_prev.push_back(srcInliers[H_F_i]);
pts_next.push_back(dstInliers[H_F_i]);
}
F_H_run8point(pts_prev, pts_next, F_H);
void F_H_run8point(const std::vector<cv::Point2f>& pts_prev, const std::vector<cv::Point2f>& pts_next, cv::Mat& F_H)
{
const int N = pts_prev.size();
assert(N >= 8);
std::vector<cv::Point2f> pts_prev_norm;
std::vector<cv::Point2f> pts_next_norm;
pts_prev_norm = pts_prev;
pts_next_norm = pts_next;
//Normalize(pts_prev, pts_prev_norm, T1);
//Normalize(pts_next, pts_next_norm, T2);
cv::Mat A(N, 18, CV_32F, Scalar(0));
for (int i = 0; i < N; ++i)
{
const float u1 = pts_prev_norm[i].x;
const float v1 = pts_prev_norm[i].y;
const float u2 = pts_next_norm[i].x;
const float v2 = pts_next_norm[i].y;
float* ai = A.ptr<float>(i);
//F
ai[0] = u1*u2;//xx'
ai[1] = u1*v2;//xy'
ai[2] = u1;//x
ai[3] = v1*u2;//yx'
ai[4] = v1*v2;//yy'
ai[5] = v1;//y
ai[6] = u2;//x'
ai[7] = v2;//y'
ai[8] = 1;//1
//H
ai[9] = 1;
ai[10] = 1;
ai[11] = 1;
ai[12] = 1;
ai[13] = 1;
ai[14] = 1;
ai[15] = 1;
ai[16] = 1;
ai[17] = 1;
}
SVD::solveZ(A, F_H);
}
H這裏我還沒有相通,代碼沒法繼續下去,
或許,
他其實就是把8個點代入F公式,求出最小誤差的F,誤差爲dif_F,然後8個點帶入H公式,誤差爲dif_H
然後求dif = dif_F+dif_H,
每次都迭代輸入8個點,求出最小誤差dif
想到了一點的
void F_H_run8point(const std::vector<cv::Point2f>& pts_prev, const std::vector<cv::Point2f>& pts_next, cv::Mat& F_H)
{
const int N = pts_prev.size();
assert(N >= 8);
std::vector<cv::Point2f> pts_prev_norm;
std::vector<cv::Point2f> pts_next_norm;
pts_prev_norm = pts_prev;
pts_next_norm = pts_next;
//Normalize(pts_prev, pts_prev_norm, T1);
//Normalize(pts_next, pts_next_norm, T2);
cv::Mat A(N, 18, CV_32F, Scalar(0));
for (int i = 0; i < N; ++i)
{
const float u1 = pts_prev_norm[i].x;
const float v1 = pts_prev_norm[i].y;
const float u2 = pts_next_norm[i].x;
const float v2 = pts_next_norm[i].y;
float* ai = A.ptr<float>(i);
//F u1=x u2=x' v1=y v2=y'
ai[0] = u1*u2;//xx'
ai[1] = u1*v2;//xy'
ai[2] = u1;//x
ai[3] = v1*u2;//yx'
ai[4] = v1*v2;//yy'
ai[5] = v1;//y
ai[6] = u2;//x'
ai[7] = v2;//y'
ai[8] = 1;//1
//H u1=x u2=x' v1=y v2=y'
ai[9] = u1;//x
ai[10] = v1;//y
ai[11] = 1;//1
ai[12] = u1;//x
ai[13] = v1;//y
ai[14] = 1;//1
ai[15] = -(u1*u2 + u1*v2);//-xx'-xy'
ai[16] = -(v1*u2 + v1*v2);//-yx'-yy'
ai[17] = -(u2 + v2);//-x'-y'
}
SVD::solveZ(A, F_H);
}
Mat F_H(18, 1, CV_32F, Scalar(0));//最後的答案
std::vector<cv::Point2f> pts_prev;
std::vector<cv::Point2f> pts_next;
for (size_t H_F_i = 0; H_F_i < 8; H_F_i++)
{
pts_prev.push_back(srcInliers[H_F_i]);
pts_next.push_back(dstInliers[H_F_i]);
}
F_H_run8point(pts_prev, pts_next, F_H);
Mat LS(9, 1, CV_32F, Scalar(0));
for (size_t i = 0; i < 9; i++)
{
cout << "i" << " " << F_H.at<float>(i, 0) << endl;
LS.at<float>(i, 0) = F_H.at<float>(i, 0);
}
LS = LS.reshape(1,3);
cout << "====================="<< endl;
Mat F_H_toF(3, 3, CV_64F, Scalar(0));
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 3; j++)
{
cout << "i" << " " << F.at<double>(i, j) << endl;
F_H_toF.at<double>(i, j) = F.at<double>(i, j);
}
}
哎不管了,再用畫出極線試一試,如果不行,就實在沒辦法了。