基于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;
}




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

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值