这里是我用opencv做的简单的灯条识别(特别简单)

  • 提取颜色
  • 灯条匹配


以下所涉及到的代码,博主已经托管至Github:https://github.com/century-yiwen/ArmorDetect

作为一个初学者,学习opencv,我直接看开源,并没有具体的看相关书籍和资料,看看开源的时候,一行一行的注释,不会的百度百度,一点一点的才开始有了眉目,最后才开始写自己的代码,这页代码写得很简单,但是博主已经觉得很开心了。

一开始拿到下面这张图片的时候:

python opencv 红色通道赋值_机器视觉


我的内心是崩溃的,因为人肉眼看到的是红色的,但是摄像头拍到的确是白色的。。。

这个问题困扰了我许久,最后某学长告诉我可以用opencv调低摄像头的曝光时间,让周围的灯光暗下来,这张可以更好的识别,所以我的摄像头参数是这样的:

VideoCapture capture(0);
	//capture.set(CV_CAP_PROP_FRAME_WIDTH, 640);//宽度
	//capture.set(CV_CAP_PROP_FRAME_HEIGHT, 480);//高度
	//capture.set(CV_CAP_PROP_FPS, 30);//帧率 帧/秒
	//capture.set(CV_CAP_PROP_BRIGHTNESS, 14);//亮度 
	//capture.set(CV_CAP_PROP_CONTRAST, 35);//对比度
	//capture.set(CV_CAP_PROP_SATURATION, 128);//饱和度
	//capture.set(CV_CAP_PROP_HUE, -13);//色调 50
	//capture.set(CV_CAP_PROP_EXPOSURE, -11);//曝光

于是乎画面呈现出来就是这样的:

python opencv 红色通道赋值_二值化_02


博主心里美滋滋啊!!!!

但是这样有个缺点,就是摄像头在3m之外就识别不到了。。。(这个问题还有待解决)

接下来就是提取颜色和匹配灯条的问题了

首先来说提取颜色,博主走过的坑

提取颜色

在这块我看了些许开源,有些是直接是进行二值化,但是这样二值化的图片的轮廓特别特别多,对于后来的匹配来说有点些许困难,于是乎,博主进行了大量的百度,哈哈~~~,,,,终于找到了一种提取轮廓的方法,于是乎博主自己写了一个函数 :

void ToHSV(Mat image, Mat result)
{
	Mat hsv_image;        //转HSV
	hsv_image.create(image.size(), image.type());
	cvtColor(image, hsv_image, CV_BGR2HSV);

	Mat mask1 = Mat(image.size(), CV_8UC1);
	Mat mask2 = Mat(image.size(), CV_8UC1);

	//result = Mat(image.size(), CV_8UC1);
	//cout << hsv_image << endl;


	/*vector<Mat> channels;
	split(hsv_image, channels);
	cout << result.row << '  '<<result.col << endl;
	int num_row = image.rows;
	int num_col = image.cols;

	for (int r = 0; r < num_row; r++)
	{
	//const Vec3b* curr_r_image = image.ptr<Vec3b>(r);
	const uchar* curr_r_hue = channels[0].ptr<uchar>(r);
	const uchar* curr_r_satur = channels[1].ptr<uchar>(r);
	const uchar* curr_r_value = channels[2].ptr<uchar>(r);
	uchar* curr_r_result = result.ptr<uchar>(r);
	for (int c = 0; c < num_col; c++)
	{
	//if (((curr_r_hue[c] <= 10 && curr_r_hue[c] >= 0)|| (curr_r_hue[c] <= 180 && curr_r_hue[c] >= 156)||(curr_r_value[c] <= 30 && curr_r_value[c] >= 0)) &&(( curr_r_value[c]<255 &&curr_r_value[c]>200))&&( curr_r_satur[c]>210 && curr_r_satur[c]<255)) //找颜色
	if ( ((curr_r_hue[c] <= 10 && curr_r_hue[c] >= 0)|| (curr_r_hue[c] <= 180 && curr_r_hue[c] >= 156)) && (curr_r_value[c]<255 && curr_r_value[c]>46) && (curr_r_satur[c]>43 && curr_r_satur[c]<255)) //找颜色

	{
	curr_r_result[c] = 255;
	}
	else
	{
	curr_r_result[c] = 0;
	}

	}
	}*/
	inRange(hsv_image, Scalar(155, 43, 35), Scalar(180, 255, 255), mask1);
	inRange(hsv_image, Scalar(0, 43, 35), Scalar(11, 255, 255), mask2);
	result = mask1 + mask2;
}

函数有两个参数,第一个参数是一个三通道的Mat,第二个参数是一个单通道的Mat,
中间注释掉的部分是,这么说的。。
在HSV的色彩模式下,利用红色所在的范围进行像素的遍历,,,如果在红色的范围内,那么在第二个单通道中就设置为白色(255),如果不在这个范围内,那么就设置为黑色(0),如此一来,这个函数就只输出一张单通道的二值化图,对于后续的提取轮廓来说就简单多了。,但是想法总是很美好的,现实总是把你的美好摁在地上摩擦~!!!这种方法一调用摄像头就失败,,偶也不知道这究竟是怎么回事,最后还是用opencv自带的函数inRang函数进行颜色的提取,真香!!!!

灯条匹配

颜色匹配完成后,下来就是灯条的匹配,在一张二值化的图中匹配一组装甲板的灯条,话不多说,上代码:

vector<RotatedRect> Armordetection(Mat image)
{
	//imshow("cap", image);
	//waitKey(30);
	Rect  external_rect;
	float area;
	Mat temp_image = image.clone();
	vector<vector<Point>>  contour;
	findContours(temp_image, contour, RETR_EXTERNAL, CHAIN_APPROX_NONE);
	vector<RotatedRect>  ellipsee(contour.size());
	vector<RotatedRect>  ellipsee1, ellipsee2, ellipsee3, ellipsee4, finally;
	int count1 = 0;
	for (int i0 = 0; i0 < contour.size(); i0++)
	{
		if (contour[i0].size()>10)
		{
			ellipsee[i0] = fitEllipse(contour[i0]);
			external_rect = ellipsee[i0].boundingRect();
			area = external_rect.area();//面积
			if (area>30 && external_rect.height>0 && external_rect.width>0 && ((double)external_rect.height / (double)external_rect.width)<6.7 && (ellipsee[i0].angle<rotate_angle || ellipsee[i0].angle>(180 - rotate_angle)))
			{
				ellipsee1.push_back(ellipsee[i0]);
				count1++;
			}
		}
	}

	int i2, j2;
	int flag = 1;
	RotatedRect temp;
	for (i2 = 1; i2 < ellipsee1.size() && flag == 1; i2++)
	{
		flag = 0;
		for (j2 = 0; j2 < ellipsee1.size() - i2; j2++)
		{
			if (ellipsee1[j2].size.height < ellipsee1[j2 + 1].size.height)
			{
				flag = 1;
				temp = ellipsee1[j2];
				ellipsee1[j2] = ellipsee1[j2 + 1];
				ellipsee1[j2 + 1] = temp;
			}
		}
	}



	int count2 = 0;
	int horizontal_angle_real;
	for (int i1 = 0; i1 < count1; i1++)
	{
		for (int j1 = i1 + 1; j1 < count1; j1++)
		{
			horizontal_angle_real = hor_angle(ellipsee1[i1].center, ellipsee1[j1].center);
			if (horizontal_angle_real < horizontal_angle && ((ellipsee1[i1].center.x - ellipsee1[j1].center.x)<780 || (ellipsee1[i1].center.x - ellipsee1[j1].center.x)>184) && (ellipsee1[i1].center.y - ellipsee1[j1].center.y)<50)
			{
				ellipsee2.push_back(ellipsee1[i1]);
				ellipsee2.push_back(ellipsee1[j1]);
				count2++;
				if (ellipsee2.size() == 2)
				{
					break;
				}
			}
		}
		if (ellipsee2.size() == 2)
		{
			break;
		}
	}
	finally = ellipsee2;
	return finally;

}

匹配灯条的过程中,先说寻找轮廓,先说两个函数minAreaRect()和fitEllipse(),在寻找过程中进行拟合,前者是拟合一个矩形,返回的是旋转矩形但是高和宽不分,计算机不认识,而后者拟合的的是椭圆,返回的旋转矩形,矩形的长是椭圆的长轴,宽是椭圆的短轴,此时的高永远比宽长,所以以后建议各位看客老爷,可以试试后者。。
我先用宽高比滤掉了一部分的矩形,,,再后来还用到了冒泡排序,将距离最近的两个灯条返回,,最后根据中心坐标点的角度和宽度进行匹配。最后得到了距离最近的一组装甲板,程序到最后将这两个矩形用线条框起来。。。

如图:

python opencv 红色通道赋值_拟合_03

python opencv 红色通道赋值_机器视觉_04

程序很简单,,,官方开源的代码太长,太复杂,反正博主没有看懂,,,各位看客老爷要是看懂的可以和我交流交流。。。

这也是我第一次写博客,内心还是有些小激动,,,大家不喜勿喷啊!

喜欢RM的道友可以交个朋友啊!