RoboMaster能量机关识别的简易实现

该代码没有使用模板匹配。

部分代码参考RoboMaster视觉教程(9)风车能量机关识别2

思路启发自内附代码|今年的大风车能量机关识别就是这么地so easy!

拟合圆函数参考OpenCV最小二乘法圆拟合

工程文件已上传https://gitee.com/seaman007/try

1.按帧读取视频

打开视频后进入while循环,把每一帧给video1,直到video1.empty()为真,并把video1克隆到video2备用。

VideoCapture video("video.avi");//打开视频
if (!video.isOpened()) 
{
	cout << "打开失败" << endl;
}
	namedWindow("识别窗口", CV_WINDOW_AUTOSIZE);
	Mat video1, video2;
while (true)
{
	video >> video1;
	if (video1.empty())
		break;
	Mat video2 = video1.clone();
}

2.图像预处理

a.二值化

分离通道后,利用通道相减channels.at(2) - channels.at(0)剔除白色的影响

vector<Mat> channels;
split(video1, channels);//分离通道
threshold(channels.at(2) - channels.at(0), video1, 100, 255, CV_THRESH_BINARY_INV);//二值化

二值化效果图
二值化效果图

b.漫水法+闭运算

利用漫水法去除多余白色,利用闭运算减少图形的数量

Mat element1 = getStructuringElement(MORPH_RECT, Size(5, 5));//设置内核1
Mat element2 = getStructuringElement(MORPH_RECT, Size(25, 25));//设置内核2
morphologyEx(video1, video1, MORPH_OPEN, element1);//开运算(使图形明显)
floodFill(video1, Point(0, 0), Scalar(0));//漫水法
morphologyEx(video1, video1, MORPH_CLOSE, element2);//闭运算(减少图形数量)

漫水法效果图
漫水法效果图
漫水法+闭运算效果图
漫水法+闭运算效果图
c.找轮廓

找轮廓用到findContours函数,各参数可自行百度

vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(video1, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);//找轮廓

d.遍历轮廓,并计算轮廓面积

int area[25] = { 0 };
for (int i = 0; i < hierarchy.size(); i++)
{
	area[i] = contourArea(contours[i]);//计算轮廓面积
}

e.利用面积筛选出目标装甲板,并绘制其最小外接矩阵及中心点

由于经过前面的闭运算,目标装甲板的面积和其他图形有较大差别,所以我们使用这种方法

if (area[i] < 2000)
{
	Point2f rect[4];
	RotatedRect box1 = minAreaRect(Mat(contours[i]));//获取最小外接矩阵
	circle(video2, Point(box1.center.x, box1.center.y), 5, Scalar(255, 0, 0), -1, 8);  //绘制最小外接矩形的中心点
	box1.points(rect);  //把最小外接矩形四个端点复制给rect数组
	for (int j = 0; j < 4; j++)
	{
		line(video2, rect[j], rect[(j + 1) % 4], Scalar(0, 255, 0), 2, 8);  //绘制最小外接矩形每条边
	}
}

绘制后效果图
绘制后效果图
f.利用最小矩形中心点拟合出圆,寻找能量机关位置

points.push_back(box1.center);//储存最小外接矩形中心点
Point2d c;//圆心
double r = 0;//半径
LeastSquaresCircleFitting(points, c, r);//拟合圆
circle(video2, c, r, Scalar(0, 0, 255), 2, 8);//绘制圆
circle(video2, c, 5, Scalar(255, 0, 0), -1, 8);//绘制圆心

绘制后效果图
绘制后效果图
拟合圆函数

int 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 1;
		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 0;
	}
	else
		return 1;
	return 0;
}

3.创建窗口,显示结果
这里显示用的是video2,这也是前面克隆video1video2备用的原因,前面所有绘制操作都在video2进行。

imshow("识别窗口", video2);
waitKey(1);

4.代码汇总

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int LeastSquaresCircleFitting(vector<cv::Point2d> &m_Points, cv::Point2d &Centroid, double &dRadius);

int main()
{
	VideoCapture video("video.avi");//打开视频
	if (!video.isOpened()) 
	{
		cout << "打开失败" << endl;
	}
	namedWindow("识别窗口", CV_WINDOW_AUTOSIZE);
	Mat video1, video2;
	
	vector<Point2d> points;

	while (true)
	{
		video >> video1;
		if (video1.empty())
			break;
		Mat video2 = video1.clone();

		vector<Mat> channels;
		split(video1, channels);//分离通道
		threshold(channels.at(2) - channels.at(0), video1, 100, 255, CV_THRESH_BINARY_INV);//二值化

		Mat element1 = getStructuringElement(MORPH_RECT, Size(5, 5));//设置内核1
		Mat element2 = getStructuringElement(MORPH_RECT, Size(25, 25));//设置内核2
		morphologyEx(video1, video1, MORPH_OPEN, element1);//开运算
		floodFill(video1, Point(0, 0), Scalar(0));//漫水
		morphologyEx(video1, video1, MORPH_CLOSE, element2);//闭运算
		
		
		vector<vector<Point>> contours;
		vector<Vec4i> hierarchy;
		findContours(video1, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);//找轮廓
		
		int area[25] = { 0 };
		for (int i = 0; i < hierarchy.size(); i++)
		{
			area[i] = contourArea(contours[i]);//计算轮廓面积
			if (area[i] < 2000)
			{
				Point2f rect[4];
				RotatedRect box1 = minAreaRect(Mat(contours[i]));//获取最小外接矩阵
				circle(video2, Point(box1.center.x, box1.center.y), 5, Scalar(255, 0, 0), -1, 8);  //绘制最小外接矩形的中心点
				box1.points(rect);  //把最小外接矩形四个端点复制给rect数组
				for (int j = 0; j < 4; j++)
				{
					line(video2, rect[j], rect[(j + 1) % 4], Scalar(0, 255, 0), 2, 8);  //绘制最小外接矩形每条边
				}
							
				points.push_back(box1.center);//储存最小外接矩形中心点
				Point2d c;//圆心
				double r = 0;//半径
				LeastSquaresCircleFitting(points, c, r);//拟合圆
				circle(video2, c, r, Scalar(0, 0, 255), 2, 8);//绘制圆
				circle(video2, c, 5, Scalar(255, 0, 0), -1, 8);//绘制圆心
			}
		}
		
		imshow("识别窗口", video2);
		waitKey(1);
	}
	return 0;
}

int 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 1;
		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 0;
	}
	else
		return 1;
	return 0;
}
海人创作于2020329

版权声明:本文为qq_45742471原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。