基于opencv椭圆识别算法的改进

其实网上有一大堆椭圆识别的例子,不管是基于霍夫算法,或者是直接ellipse,都会遇到一些问题。当然,有那种上千行代码的例子,我也没仔细看。下面就是百来行代码对算法的改进。
这里主要是针对在比较复杂的场景,直接对ellipse算法的改进,再具体一点,就是在椭圆过滤上加上一些其他的算法。但是由于不同场景亮度,椭圆大小,场景复杂度不同,这些算法不确保每种场景都适用,具体场景需要设置不同的参数。
遇到的问题主要如下,都是针对比较复杂的场景,干扰因素较多的情况:
首先,在许多人选择识别椭圆时会首先将图片灰度处理,然后进行threshold二值化处理。但是,实际生活中,二值化的取值相对来说不稳定,很难将椭圆或者圆完整的过滤出来,所以,这里首先得一步,是不进行二值化处理,直接将灰度处理后的图片,进行椭圆识别处理。
1.用canny调参数时,会直接过滤掉识别的椭圆,这里主要采取的做法是,调低最大和最低threshold值,使原始图片过滤后尽可能有椭圆的轮廓,其他过滤放在后面进行。
2.传统过滤方式会采用点数量过滤,轮廓面积过滤,但是,在这里,点的数量和轮廓面积是极其不靠谱的。主要是由于上述步骤得到轮廓点后,并不能完全保证闭合,所以点的数量不能保证,并且面积也只是起点到终点连线的轮廓面积。解决方法是在第一轮过滤的基础上再加一轮过滤,下面再讲具体实现。这一步只是放宽过滤条件,确保要识别的椭圆能不被过滤掉。
3.上述过滤完后,会采取椭圆长短轴比较。随后,就用ellipse函数将椭圆画出来。但是,在较复杂的场景中,矩形通常也会被识别为椭圆,所以这里需要加一步过滤方式,有两种选择,第一种,采用凹凸性原理。第二种,采用直线斜率处处相等原则。
4.上述过滤完后,会发现有许多大大小小的椭圆被识别出来,所以这里需要加第二轮椭圆过滤,需要一张黑色背景,将第一轮得到的椭圆画在黑色背景上,椭圆必须得填充,不能只留有线性,因为在后面过滤中,canny算子会识别出内外两个轮廓。然后再进行过滤。
这些提到的东西都是目前项目里面涉及到的,并且还涉及一些专利和论文。所以要用到这些东西,务必留言,代码就放这了,参数需要自己改,就不多解释了,后面可能会删除这文章。你们可以用这段代码,修改参数,识别下图中的椭圆。

#include<iostream>
#include"opencv.hpp"

using namespace std;
using namespace cv;


void drawCross(Mat &img, Point2f point, Scalar color, int size, int thickness = 1)
{
	//绘制横线  
	line(img, cvPoint(point.x - size / 2, point.y), cvPoint(point.x + size / 2, point.y), color, thickness, 8, 0);
	//绘制竖线  	
	line(img, cvPoint(point.x, point.y - size / 2), cvPoint(point.x, point.y + size / 2), color, thickness, 8, 0);
	return;
}


//提取单幅图像的特征点
void FeaturePoint(Mat &img, Mat &img2)
{
	//threshold(img, img, 40, 255, CV_THRESH_BINARY_INV);
	//将图片换为其它颜色
	Point2f center; //定义变量
	Point2f center1; //定义变量
	Mat edges;
	GaussianBlur(img, edges, Size(5, 5), 0, 0);
	//Canny(edges, edges, 35, 120, 3);
	Canny(edges, edges,35, 70, 3);
	Mat mm = img2(Rect(0, 0, img2.cols , img2.rows));//ROI设置
	mm = { Scalar(0, 0, 0) };//把ROI中的像素值改为黑色
	vector<vector<Point> > contours;// 创建容器,存储轮廓
	vector<Vec4i> hierarchy;// 寻找轮廓所需参数

	findContours(edges, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	Mat imageContours = Mat::zeros(edges.size(), CV_8UC1);//轮廓
	for (int i = 0; i < contours.size(); i++)
	{
		//绘制轮廓 
		drawContours(imageContours, contours, i, Scalar(255), 1, 8, hierarchy);
	}

	//过滤
	for (int i = 0; i < contours.size(); i++)
	{
		if (contours[i].size() <= 100 || contours[i].size() >= 1000){
			continue;
		}
		
		if (contourArea(contours[i]) < 10 || contourArea(contours[i]) > 40000){
			continue;
		}
		//利用直线斜率处处相等的原理
		/*if (abs(((double)(contours[i][0].y - contours[i][20].y) / (double)(contours[i][0].x - contours[i][20].x) -
			(double)(contours[i][20].y - contours[i][40].y) / (double)(contours[i][20].x - contours[i][40].x)))<0.2)
				continue;*/
		//利用凹凸性的原理
		if (!abs((contours[i][0].y + contours[i][80].y) / 2 - contours[i][40].y))
			continue;

		RotatedRect m_ellipsetemp;  // fitEllipse返回值的数据类型
		m_ellipsetemp = fitEllipse(contours[i]);  //找到的第一个轮廓,放置到m_ellipsetemp
		if (m_ellipsetemp.size.width / m_ellipsetemp.size.height < 0.3)
		{
			continue;
		}
		cout << "1:" << contourArea(contours[i]) << endl;
		cout << "2:" << contours[i].size() << endl;
		//drawCross(img, center, Scalar(255, 0, 0), 30, 2);
		//ellipse(img, m_ellipsetemp, cv::Scalar(255, 255, 255));   //在图像中绘制椭圆
		ellipse(mm, m_ellipsetemp, cv::Scalar(255, 255, 255), CV_FILLED);   //在图像中绘制椭圆
	}
	
	Mat edges1;
	GaussianBlur(mm, edges1, Size(5, 5), 0, 0);
	Canny(edges1, edges1, 50, 150, 3);
	//vector<vector<Point> > contours1;// 创建容器,存储轮廓
	//vector<Vec4i> hierarchy1;// 寻找轮廓所需参数
	findContours(edges1, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	Mat imageContours1 = Mat::zeros(edges1.size(), CV_8UC1);//轮廓

	for (int i = 0; i < contours.size(); i++)
	{
		drawContours(imageContours1, contours, i, Scalar(255), 1, 8, hierarchy);
		//绘制轮廓 
	}
	for (int i = 0; i < contours.size(); i++)
	{
		if (contours[i].size() <= 100 || contours[i].size() >= 1000){
			continue;
		}
		//利用凹凸性的原理
		/*if (!abs((contours[i][0].y + contours[i][20].y) / 2 - contours[i][10].y))
			continue;*/

		if (contourArea(contours[i]) < 3000 || contourArea(contours[i]) > 20000){
			continue;
		}
		RotatedRect m_ellipsetemp1;  // fitEllipse返回值的数据类型
		m_ellipsetemp1 = fitEllipse(contours[i]);  //找到的第一个轮廓,放置到m_ellipsetemp
		ellipse(img, m_ellipsetemp1, cv::Scalar(255, 0, 0));   //在图像中绘制椭圆
		cout << "mianji" << contourArea(contours[i]) << endl;
		cout << "dianshu" << contours[i].size() << endl;
		/*cout << (float)contours[i][0].x << endl;
		cout << contours[i][0].y << endl;*/
		center1 = m_ellipsetemp1.center;//读取椭圆中心
		drawCross(img, center1, Scalar(255, 0, 0), 30, 2);
		cout << center1.x << ends << center1.y << endl;
	}
	/*imshow("image", img1);
	waitKey(0);*/
	//return ;//返回椭圆中心坐标

}

int main(int argc, char* argv[])
{
	const char* imagename = "pixmap.png";

	//从文件中读入图像
	Mat img = imread(imagename, 1);
	Mat img2 = imread(imagename, 1);
	//如果读入图像失败
	if (img.empty())
	{
		fprintf(stderr, "Can not load image %s\n", imagename);
		return -1;
	}
	FeaturePoint(img ,img2);
	//显示图像
	imshow("image", img);
	//此函数等待按键,按键盘任意键就返回
	waitKey();
	return 0;
}




通过上面代码过滤,识别图中椭圆

<think>好的,我现在需要帮用户解决如何使用OpenCV实现椭圆检测与识别的问题。首先,用户提到了参考中的一些代码片段,比如形态学处理和仿射变换,这些可能在预处理阶段有用。我得先理清椭圆检测的一般步骤,然后结合OpenCV的功能来实现。 首先,椭圆检测通常包括图像预处理、边缘检测、轮廓提取、椭圆拟合这几个步骤。用户提供的引用里提到了形态学操作,比如膨胀,这可能用于去除噪声或连接断开的边缘,这对椭圆检测有帮助。引用3提到使用特定标志物,比如黑白同心圆环,这可能意味着需要检测多个椭圆或区分不同的椭圆。 接下来,我需要考虑OpenCV中的相关函数。例如,Canny边缘检测是常用的方法,然后是findContours来找到轮廓。不过,椭圆检测可能需要更精确的方法,因为椭圆拟合对轮廓的要求较高。另外,OpenCV中有fitEllipse函数可以直接对轮廓进行椭圆拟合,但它的准确性依赖于轮廓的质量。 用户提供的引用1中的膨胀操作可能是在预处理阶段增强椭圆边缘的连接性。引用2提到了标记边缘点,可能涉及到霍夫变换之类的算法,但霍夫椭圆检测在OpenCV中可能计算量较大,需要优化。 可能的步骤是:读取图像,转为灰度,预处理(比如高斯模糊去噪),边缘检测,形态学操作(膨胀、腐蚀),查找轮廓,对每个轮廓尝试椭圆拟合,然后筛选符合条件的椭圆。 需要注意的问题包括:预处理参数的选择(如高斯模糊的核大小),边缘检测的阈值设置,轮廓过滤的条件(面积、形状等),以及椭圆拟合后的验证(比如椭圆的长短轴比例,中心位置等)。 用户可能需要具体的代码示例,所以需要提供一个分步骤的代码框架,并解释每个步骤的作用。同时,要考虑性能问题,比如使用霍夫变换可能较慢,而fitEllipse方法更快但需要准确的轮廓。 另外,引用3提到的黑白同心圆可能涉及检测多个椭圆,并且需要区分它们,可能需要根据椭圆参数进行筛选,比如中心位置相近的不同椭圆。 总结起来,步骤大概是: 1. 图像读取与灰度化 2. 预处理(去噪、二值化、形态学操作) 3. 边缘检测或轮廓查找 4. 对每个轮廓应用椭圆拟合 5. 筛选有效的椭圆 6. 绘制或输出结果 在代码实现中,需要用到cv2.cvtColor、cv2.GaussianBlur、cv2.Canny、cv2.findContours、cv2.fitEllipse等函数。同时,可能需要调整参数,比如Canny的阈值,轮廓的最小面积等,以提高检测准确率。 现在需要考虑用户可能的疑问,比如如何处理噪声干扰,多个椭圆的区分,拟合失败的情况,以及性能优化。此外,用户可能需要了解如何验证检测到的椭圆是否正确,比如通过绘制椭圆到图像上并显示。 最后,生成相关问题时要围绕椭圆检测的步骤、优化方法、参数调整、应用场景等方面提问,帮助用户深入理解。</think>### OpenCV椭圆检测实现步骤 #### 一、图像预处理 1. **灰度化与降噪** 使用$cv2.cvtColor()$将图像转为灰度,并用高斯模糊$cv2.GaussianBlur()$消除噪声: ```python img = cv2.imread('target.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (5,5), 0) ``` 2. **形态学操作**(可选) 引用中提到的膨胀操作可增强边缘连续性[^1]: ```python kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9)) dilated = cv2.dilate(blurred, kernel, iterations=2) ``` #### 二、边缘检测与轮廓提取 ```python edges = cv2.Canny(dilated, 50, 150) # 调整阈值控制边缘细节 contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) ``` #### 三、椭圆拟合与筛选 ```python valid_ellipses = [] for cnt in contours: if len(cnt) >= 5: # 至少5个点才能拟合椭圆 ellipse = cv2.fitEllipse(cnt) (cx,cy), (a,b), angle = ellipse # 筛选条件:长短轴比例、面积等 if 0.8 < a/b < 1.2 and a > 20: valid_ellipses.append(ellipse) ``` #### 四、结果可视化 ```python for e in valid_ellipses: cv2.ellipse(img, e, (0,255,0), 2) cv2.imshow('Result', img) cv2.waitKey(0) ``` #### 五、霍夫椭圆检测(备选方案) ```python # 需OpenCV 4.8+版本支持 ellipses = cv2.HoughEllipses(edges, method=1, dp=1.5, minDist=20) ``` --- ### 关键技术说明 1. **形态学操作**:膨胀可修复断裂边缘,腐蚀可消除细小噪点 2. **轮廓筛选**:通过面积、周长、凸性等参数排除干扰轮廓 3. **椭圆验证**:检查拟合椭圆的长短轴比例、中心点聚集性(适用于同心圆场景)[^3] --- §§ 相关问题 §§ 1. 如何解决椭圆检测中多个同心圆相互干扰的问题? 2. 怎样优化椭圆检测算法在低光照图像中的表现? 3. OpenCV的霍夫椭圆检测与最小二乘拟合方法有何性能差异? 4. 工业场景中椭圆检测的精度如何校准?
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值