该代码没有使用模板匹配。
部分代码参考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,这也是前面克隆video1到video2备用的原因,前面所有绘制操作都在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;
}
海人创作于2020年3月29日
版权声明:本文为qq_45742471原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。