关于OpenCV的那些事——利用RANSAC消除错误姿态

上一节《关于OpenCV的那些事——跟踪点选取方式和特征点跟踪恢复》讲了两种跟踪和恢复的方法,这一篇主要讲第一个优化,使用random sample consensus收敛相机姿态。下一篇讲使用最小二乘多项式平滑消除姿态抖动。


我们知道在计算相机姿态的时候,opencv中提供了两种函数:solvePnP, solvePnPRansac。 第二个函数即是利用ransac的思想计算更加精确的姿态。 鉴于之前章节《关于OpenCV的那些事——相机姿态更新》里讲到的相机姿态更新至少使用4组2D/3D点对,我们自己试着实现一下RANSAC。 思想是对于追踪的n个特征点对,我们先随机生成m个4组点对(m < Cn4(排列组合)),分别计算出m个姿态,然后对于每一个姿态计算重投影误差,小于一定阀值的记录下来,并更新最佳姿态(最小误差),最终返回这个最佳姿态。m也叫迭代次数。当然选择合适的m,既能节省时间,有能找到最佳姿态。重投影误差的阀值也需要做实验找到最合适的。


C++代码如下:

bool collinear_ornot(Point2f p1, Point2f p2, Point2f p3)   // 三点是否共线
{
	if (abs((p2.x - p1.x)*p3.y - (p2.y - p1.y)*p3.x - p1.y*p2.x + p1.x*p2.y) < 1e-5)
		return true;
	else
		return false;
}
void random_n_4p(vector<Point2f>& imgP)
{
	srand((unsigned)time(NULL));
	int n = imgP.size();           //n为追踪的特征点的个数
	for (int i = 0; i < ransac_1; i++)    // ransac_1为上文中的m迭代次数,本项目中取20
	{
		do
		{
			n_4[i][0] = rand() % n;
			do
			{
				n_4[i][1] = rand() % n;
			} while (n_4[i][1] == n_4[i][0]);
			do
			{
				n_4[i][2] = rand() % n;
			} while (n_4[i][2] == n_4[i][1] || n_4[i][2] == n_4[i][0]);
			do
			{
				n_4[i][3] = rand() % n;
			} while (n_4[i][3] == n_4[i][2] || n_4[i][3] == n_4[i][1] || n_4[i][3] == n_4[i][0]);
		}while (collinear_ornot(imgP[n_4[i][0]], imgP[n_4[i][1]], imgP[n_4[i][2]]) || collinear_ornot(imgP[n_4[i][0]], imgP[n_4[i][1]], imgP[n_4[i][3]]) || collinear_ornot(imgP[n_4[i][1]], imgP[n_4[i][2]], imgP[n_4[i][3]]));
	}       //确保四点中每每三点不共线
}
void ransac_cc(vector<Point2f>& imgP, Mat& r, Mat& t)
{
	random_n_4p(imgP);
	
	vector<Point2f> temp4imgP;
	vector<Point3f> temp4objP;
	vector<Point2f> temprepP;
	temprepP.resize(imgP.size());
	Mat tempobjPM;
	vector<double> temprv(3), temptv(3), bestr(3),bestt(3);
	Mat temp_r(temprv),temp_t(temptv),best_r(bestr),best_t(bestt);
	float minerror = 1,temperror = 0, errorthreshold = 1;   //重投影误差阀值为1 pixel
	float testerror = 0;
	for (size_t iteration = 0; iteration < ransac_1; iteration++)
	{
		temp4imgP.push_back(imgP[n_4[iteration][0]]);
		temp4imgP.push_back(imgP[n_4[iteration][1]]);
		temp4imgP.push_back(imgP[n_4[iteration][2]]);
		temp4imgP.push_back(imgP[n_4[iteration][3]]);
		temp4objP.push_back(objP[n_4[iteration][0]]);
		temp4objP.push_back(objP[n_4[iteration][1]]);
		temp4objP.push_back(objP[n_4[iteration][2]]);
		temp4objP.push_back(objP[n_4[iteration][3]]);
		
		Mat(temp4objP).convertTo(tempobjPM, CV_32F);
		solvePnP(tempobjPM, Mat(temp4imgP), camera_matrix, distortion_coefficients, temp_r, temp_t);
		projectPoints(objPM, temp_r, temp_t, camera_matrix, distortion_coefficients, temprepP);
		testerror = norm(imgP, temprepP, NORM_L2);
		for (size_t i=0; i < imgP.size();i++)
		{
			temperror += sqrt((imgP[i].x - temprepP[i].x)*(imgP[i].x - temprepP[i].x) + (imgP[i].y - temprepP[i].y)*(imgP[i].y - temprepP[i].y));
		}
		if (temperror < errorthreshold)       //重投影误差阀值为1 pixel
		{
			if (temperror < minerror)
			{
				minerror = temperror;
				best_r = temp_r;
				best_t = temp_t;
			}
		}
		temp4imgP.clear();
		temp4objP.clear();
		tempobjPM.setTo(0);
		temperror = 0;
		testerror = 0;
	}
	r = best_r;
	t = best_t;
}

通过找到最小重投影误差,我们找到了最佳姿态,而且速度还行。下一篇讲使用最小二乘多项式平滑消除姿态抖动。


————————————————

版权声明:本文为CSDN博主「cc_sunny」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/aptx704610875/java/article/details/51200583



本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年4月    »
1234567
891011121314
15161718192021
22232425262728
2930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864