[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

解决一个问题,关于在博客:

关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

中的论文实现:

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

随机抽取8个不同点,使用上图公式(5),求取F矩阵和H矩阵

公式中左边F那一块最小值为0,右边H那一块最小值为0,整体公式E其实是一个优化问题

方式通过最小二乘估计求解方程组,AX=0

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

#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 );	
} 

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

为什么H矩阵的自由度为8:

假设两张图像中的对应点对齐次坐标为(x',y',1)和(x,y,1),单应矩阵H定义为:

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

矩阵展开后有3个等式,将第3个等式代入前两个等式中可得:

(下面的式子这么写是因为要除以1,那个1的表示就是下面式子中的分母,为什么除以1,因为齐次坐标系)

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

也就是说,一个点对对应两个等式。在此插入一个讨论:单应矩阵H有几个自由度?

或许有人会说,9个啊,H矩阵不是9个参数吗?从h11到h33总共9个。真的是这样吗?实际上并不是,因为这里使用的是齐次坐标系,也就是说可以进行任意尺度的缩放。比如我们把hij乘以任意一个非零常数k并不改变等式结果:

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

如果这里,存在一种情况,k的值,正好等于,1/h33,仅仅是值相等

就是不管h33是什么未知数,因为k可以是任意非0数,所以肯定有一种尺度的缩放,使其k的值和1/h33相等

所以实际上单应矩阵H只有8个自由度。8自由度下H计算过程有两种方法。

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

通过上述讲解,我相信大家已经明白了,H的自由度为8,所以,实际运算中,方便理解期间,我们直接把h33设为1

接着,我们把F矩阵写出来:

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

F矩阵的自由度为7,和H矩阵有一点一样,可以将f9,直接设置为1,因为F矩阵也是在齐次坐标系下

H矩阵有两个公式,4个匹配对,就可解决8自由度的H矩阵的问题

F矩阵只有一个公式,我们一般去解决问题时,

视F矩阵为8自由度,不去管F矩阵还应该找到的一个自由度了,直接把f9设置为1,8个匹配对,就可解决8自由度的F矩阵的问题

这种方法,很多博客称为8点法,求解F矩阵

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

别的博客公开的GitHub代码:

https://blog.****.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


想到了一点的

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

[再再再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

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);
		}
	}

哎不管了,再用画出极线试一试,如果不行,就实在没办法了。