OpenCV最小二乘法圆拟合

版权声明:本文为博主原创文章,转载请标明出处,蟹蟹 https://blog.csdn.net/fly_wt/article/details/86182916

纸上得来终觉浅,绝知此事要躬行!

时至今日我才发现做项目的意义所在,确实只有实战才能验证自己学的到底怎么样,事实证明,我目前学的狗屁不是,眼高手低,而且没有静下心来好好搞科研,甚是浮躁,没有仔细思考,我今天深刻检讨自己。
项目设计的图像处理,就是从一堆圆环中提取想要的圆心和半径,但是由于零件本身的缺陷以及光照问题导致的噪声让问题处理起来相当棘手,理论学了很多,真正用起来确实需要大量实验和猜想来得到想要的效果,所以从今天起要逐渐养成好习惯:

  • 做实验要控制变量记录好数据
  • 明确主次,条理清晰
  • 每天总结一天所为

今天总结一下遇到你和圆的问题

最小二乘法圆拟合

圆心拟合常用的方法有霍夫变换法和最小二乘法。利用霍夫变换进行圆心拟合,需要对每一个检测点计算,时间较长且拟合不准确。这里浪费我大量时间来调参。最小二乘法是在随机误差为正态分布时,由最大似然法推出的最优估计技术,它可以使测量误差的平方和最小,适用于从一组测量值中求出一组未知量。设圆的表达式为
(xa)2+(yb)2=r2(x-a)^2+(y-b)^2=r^2,令:
在这里插入图片描述
计算得,
在这里插入图片描述
式中 N 为参与拟合的边界点的总个数; i 范围为[0,N],X和 Y 分别为各个边界点的横坐标和纵坐标。在 OpenCV中调用 draw Contours( ) 函数和 circle( ) 函数,绘制识别的轮廓和拟合的圆心。
C++实现如下:

BOOL LeastSquaresCircleFitting(vector<cv::Point2d> &m_Points, cv::Point2d &Centroid, double &dRadius)
{
	if (!m_Points.empty())
	{
		int iNum = (int)m_Points.size();
		if (iNum < 3)	return FALSE;
		double X1 = 0.0;
		double Y1 = 0.0;
		double X2 = 0.0;
		double Y2 = 0.0;
		double X3 = 0.0;
		double Y3 = 0.0;
		double X1Y1 = 0.0;
		double X1Y2 = 0.0;
		double X2Y1 = 0.0;
		vector<cv::Point2d>::iterator iter;
		vector<cv::Point2d>::iterator end = m_Points.end();
		for (iter = m_Points.begin(); iter != end; ++iter)
		{
			X1 = X1 + (*iter).x;
			Y1 = Y1 + (*iter).y;
			X2 = X2 + (*iter).x * (*iter).x;
			Y2 = Y2 + (*iter).y * (*iter).y;
			X3 = X3 + (*iter).x * (*iter).x * (*iter).x;
			Y3 = Y3 + (*iter).y * (*iter).y * (*iter).y;
			X1Y1 = X1Y1 + (*iter).x * (*iter).y;
			X1Y2 = X1Y2 + (*iter).x * (*iter).y * (*iter).y;
			X2Y1 = X2Y1 + (*iter).x * (*iter).x * (*iter).y;
		}
		double C = 0.0;
		double D = 0.0;
		double E = 0.0;
		double G = 0.0;
		double H = 0.0;
		double a = 0.0;
		double b = 0.0;
		double c = 0.0;
		C = iNum * X2 - X1 * X1;
		D = iNum * X1Y1 - X1 * Y1;
		E = iNum * X3 + iNum * X1Y2 - (X2 + Y2) * X1;
		G = iNum * Y2 - Y1 * Y1;
		H = iNum * X2Y1 + iNum * Y3 - (X2 + Y2) * Y1;
		a = (H * D - E * G) / (C * G - D * D);
		b = (H * C - E * D) / (D * D - G * C);
		c = -(a * X1 + b * Y1 + X2 + Y2) / iNum;
		double A = 0.0;
		double B = 0.0;
		double R = 0.0;
		A = a / (-2);
		B = b / (-2);
		R = double(sqrt(a * a + b * b - 4 * c) / 2);
		Centroid.x = A;
		Centroid.y = B;
		dRadius = R;
		return TRUE;
	}
	else
		return FALSE;
	return TRUE;

}

参考文献
赵 炯,朱海涛,屈剑平,张根雷. 基于 OpenCV 的圆心定位在地铁隧道变形监测中的应用, 同济大学 机械与能源工程学院,2013 年 第 32 卷 第 11 期

展开阅读全文

没有更多推荐了,返回首页