OpenCV最小二乘法圆拟合

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

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

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

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

最小二乘法圆拟合

圆心拟合常用的方法有霍夫变换法和最小二乘法。利用霍夫变换进行圆心拟合,需要对每一个检测点计算,时间较长且拟合不准确。这里浪费我大量时间来调参。最小二乘法是在随机误差为正态分布时,由最大似然法推出的最优估计技术,它可以使测量误差的平方和最小,适用于从一组测量值中求出一组未知量。设圆的表达式为
( x − a ) 2 + ( y − b ) 2 = r 2 (x-a)^2+(y-b)^2=r^2 (xa)2+(yb)2=r2,令:
在这里插入图片描述
计算得,
在这里插入图片描述
式中 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 期

  • 10
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
OpenCV中提供了函数fitLine()来实现最小二乘法拟合直线。 使用方法如下: 1. 导入必要的库和模块: ```python import cv2 import numpy as np ``` 2. 准备数据: 我们需要提供一些点的坐标,用于拟合直线。这些点可以是图像中的像素点,也可以是其他任意坐标系中的点。 ```python # 生成一些随机点 points = np.random.randint(0, 200, (10, 2)) ``` 3. 拟合直线: 使用fitLine()函数来拟合直线,该函数需要传入一些参数: - points:需要拟合的点的坐标。 - distType:点到直线的距离类型,可以选择CV_DIST_L1, CV_DIST_L2或CV_DIST_L12。 - param:累加器分辨率与rho的比值,一般取值为1。 - reps:在一条直线上的最小点数,一般取值为0。 - aeps:角度的最小精度,一般取值为0.01度。 ```python # 使用fitLine()函数拟合直线 vx, vy, x, y = cv2.fitLine(points, cv2.DIST_L2, 0, 0.01, 0.01) ``` 4. 绘制直线: 得到直线的参数后,我们可以使用这些参数来绘制直线。可以使用cv2.line()函数来实现。 ```python # 绘制直线 lefty = int((-x*vy/vx) + y) righty = int(((200-x)*vy/vx)+y) cv2.line(img, (199,righty), (0,lefty), (0,255,0), 2) ``` 完整代码如下: ```python import cv2 import numpy as np # 生成一些随机点 points = np.random.randint(0, 200, (10, 2)) # 使用fitLine()函数拟合直线 vx, vy, x, y = cv2.fitLine(points, cv2.DIST_L2, 0, 0.01, 0.01) # 绘制直线 img = np.zeros((200, 200, 3), dtype=np.uint8) lefty = int((-x*vy/vx) + y) righty = int(((200-x)*vy/vx)+y) cv2.line(img, (199,righty), (0,lefty), (0,255,0), 2) cv2.imshow("Fit Line", img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 运行结果如下图所示: ![拟合直线结果](https://img-blog.csdnimg.cn/20210622152054570.png)
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

量子孤岛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值