写在前面的话:
算是在RM这个大队伍里待了一年啦,从刚开始的懵懵懂懂,到现在的一知半解,也算是收获了很多,非常感谢江达小记的大佬给我点明了方向,现在算是勉勉强强将自己改过的代码贴出来,因为手边没有装甲板模块,可能会有点小问题,希望大佬指出!陈四海在此献丑了!
本文只在代码中进行了详解,有关图解会在后面进一步更新,欢迎关注或私信我催更!
main.cpp
/**************************************
Copyright 陈四海
Author: created by 陈四海
Date: 2021-01-21
Description: 装甲板识别
Version: 1.0
**************************************/
#include "armor_plate.h"
bool CameraRead(ArmorPlate& armor_src);
int main() {
ArmorPlate armor;
while (true) {
if (!CameraRead(armor))
continue;
if (waitKey(1) == 'r')
armor.enemy_color = REDENEMY;
if (waitKey(1) == 'b')
armor.enemy_color = BLUEENEMY;
armor.autoShoot();
}
return 0;
}
/**************************************
Fuction: CameraRead
Description:
Input: ArmorPlate& armor_src
Output:
Return: bool
Others:
**************************************/
bool CameraRead(ArmorPlate& armor_src){
armor_src.capture.read(armor_src.src_image);
if (!armor_src.src_image.data) {
cout << "The camera has not read a image" << endl;
armor_src.cameraInit(0);//0是打开自己的摄像头,1是打开其他摄像头
return false;
}
else
return true;
}
armor_plate.h
#pragma once
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
#define REDENEMY 2
#define BLUEENEMY 0
class ArmorPlate {
public:
//元素
ArmorPlate();
int enemy_color;
Mat src_image; //原图
VideoCapture capture; //摄像头捕抓到的像素
bool cameraInit(int device); //摄像头初始化
void autoShoot(); //调用自瞄
private:
Mat hsv_img; //HSV通道图像
vector<Mat> hsv_split; //HSV分离后图像
Mat dst_image; //
vector<Mat> channels; //
Mat gray_img;
double max_value;
vector<vector<Point>> light_contours;//灯条轮廓
vector<RotatedRect> light_infos;
vector<RotatedRect> armors;
vector<RotatedRect> armor_rects;
RotatedRect armor_rect;
void imgPreprosses(const Mat& src);
};
ArmorPlate.cpp
#include <opencv2/opencv.hpp>
#include "armor_plate.h"
//#define DEBUG_SHOW
//#define DEBUG_CONTOURS
ArmorPlate::ArmorPlate() {
enemy_color = REDENEMY;//默认敌方为红色
}
/**************************************
Fuction: CamaraInit
Description: 摄像头初始化
Input: device
Output:
Return: false or true
Others: bool
**************************************/
bool ArmorPlate::cameraInit(int device)
{
capture.open(device);
if (!capture.isOpened())//检测capture_armor中是否有数据
{
cout << "The capture has something wrong!";
return false;
}
else return true;
}
/**************************************
Fuction: adjustRec
Description: 矫正灯条
Input: rec
Output:
Return:
Others: RotatedRect
**************************************/
RotatedRect& adjustRec(cv::RotatedRect& rec)//矫正灯条
{
//using std::swap;
float& width = rec.size.width;
float& height = rec.size.height;
float& angle = rec.angle;
while (angle >= 90.0) angle -= 180.0;
while (angle < -90.0) angle += 180.0;
if (angle >= 45.0)
{
swap(width, height);
angle -= 90.0;
}
else if (angle < -45.0)
{
swap(width, height);
angle += 90.0;
}
return rec;
}
/**************************************
Fuction: drawall
Description: 画出矩阵
Input: rec,img
Output:
Return:
Others: void
**************************************/
void drawall(vector<RotatedRect> rec, Mat img)
{
for (int i = 0; i < rec.size(); i++)
{
Point2f p[4];
rec[i].points(p);//返回四个点的值
line(img, p[0], p[1], Scalar(0, 0, 255), 1, 8, 0);
line(img, p[1], p[2], Scalar(0, 0, 255), 1, 8, 0);
line(img, p[2], p[3], Scalar(0, 0, 255), 1, 8, 0);
line(img, p[3], p[0], Scalar(0, 0, 255), 1, 8, 0);
}
}
/**************************************
Fuction: autoShoot
Description: 自瞄
Input:
Output:
Return:
Others: void
**************************************/
void ArmorPlate::autoShoot()
{
//Mat s = imread("D:/A-视觉/练习/RM_chen/1.jpg");
imgPreprosses(src_image);
}
/**************************************
Fuction: imgPreprosses
Description: 装甲板识别
Input: src,dst
Output:
Return:
Others: void
**************************************/
void ArmorPlate::imgPreprosses(const Mat& src)//HSV版本
{
cvtColor(src, hsv_img, COLOR_BGR2HSV); //色彩空间转换成HSV通道
split(hsv_img, hsv_split); //分类原图像的HSV三通道
equalizeHist(hsv_split[2], hsv_split[2]); //对HSV的亮度通道进行直方图均衡
merge(hsv_split, hsv_img); //合并三种通道
cvtColor(hsv_img, dst_image, COLOR_HSV2BGR); //将HSV空间转回至RGB空间,为接下来的颜色识别做准备
split(dst_image, channels);
//通道相减识别不出来就换成单通道或者hsv方法
if (!enemy_color) { //通道相减->敌方红色
gray_img = channels.at(0) - channels.at(2);
blur(gray_img, gray_img, Size(3, 3));
}
else //通道相减->敌方蓝色
gray_img = channels.at(2) - channels.at(0);
minMaxLoc(gray_img, 0, &max_value, 0, 0); //二值化阈值
threshold(gray_img, gray_img, max_value * 0.98, 255, THRESH_BINARY);//二值化
medianBlur(gray_img, gray_img, 3); //中值滤波
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));//用于确定核的大小
morphologyEx(gray_img, gray_img, MORPH_DILATE, element, Point(-1, -1), 1);//形态学变换,开闭运算
findContours(gray_img.clone(), light_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);//轮廓提取
#ifndef DEBUG_CONTOURS//绘制轮廓
Mat drawingImg = Mat::zeros(src.size(), CV_8UC3);
for (int i = 0; i < light_contours.size(); i++)
drawContours(drawingImg, light_contours, i, Scalar(0, 255, 0));
imshow("轮廓图", drawingImg); waitKey(1);
#endif // !DEBUG_CONTOURS
for (const auto& contour : light_contours)//对每个轮廓都进行处理
{
//得到轮廓的面积
float light_contour_area = contourArea(contour);
//面积太小的不要
if (contour.size() <= 5 ||
light_contour_area < 10) continue;
//椭圆拟合生成相应的旋转矩形(注:只是用矩形去拟合灯条,方便获取灯条的长宽比等)
RotatedRect light_rec = fitEllipse(contour);
//RotatedRect minAreaRec = minAreaRect(contour);
//矫正灯条
adjustRec(light_rec);
//宽高比、凸度筛选灯条
if ((light_rec.size.width / light_rec.size.height) > 0.8)
continue;
float x = light_rec.center.x - light_rec.size.width;
if (x < 0)
continue;
float y = light_rec.center.y - light_rec.size.height;
if (y < 0)
continue;
//筛选出需要的灯条
if (light_rec.size.width / light_rec.size.height > 1.0 ||
light_contour_area / light_rec.size.area() < 0.5)
continue;
//对灯条范围适当扩大
light_rec.size.width *= 1.1;
light_rec.size.height *= 1.1;
//判断角度,将符合的灯条进行保存
if ((light_rec.size.height > 10 && (light_rec.size.height < 150) && (light_rec.angle < 45 || light_rec.angle>135)))
light_infos.push_back(light_rec);
}
armors.clear();
armor_rects.clear();
//灯条信息检测
if (light_infos.size() <= 1)
{
cout << "There's no light contours in quality." << endl;
}
//按灯条中心x从小到大排序
sort(light_infos.begin(), light_infos.end(), [](const RotatedRect& ld1, const RotatedRect& ld2)
{
return ld1.center.x < ld2.center.x;
});
for (int i = 0; i < light_infos.size(); i++)
{//遍历所有灯条进行匹配
for (int j = i + 1; j < light_infos.size(); j++)
{
const RotatedRect& left = light_infos[i];//左灯条
const RotatedRect& right = light_infos[j];//右灯条
//比对高度
float heightDiff = abs(left.size.height - right.size.height);
//比对宽
float widthDiff = abs(left.size.width - right.size.width);
//比对角度
float angleDiff = abs(left.angle - right.angle);
//长度差比率
//float LenDiff_ratio = abs(leftLight.length - rightLight.length) / max(leftLight.length, rightLight.length);
//筛选
//东南大学那个我实在懒得看了所以就把LenDiff_ratio删掉了
//左右灯条相距距离
float dis = sqrt((left.center.x - right.center.x) * (left.center.x - right.center.x) + (left.center.y - right.center.y) * (left.center.y - right.center.y));
//左右灯条高度的平均值
float meanheight = (left.size.height + right.size.height) / 2;
//左右灯条中心点y的差值
float yDiff = abs(left.center.y - right.center.y);
//y差比率
float yDiffRatio = yDiff / meanheight;
//左右灯条中心点x的差值
float xDiff = abs(left.center.x - right.center.x);
//x差比率
float xDiffRatio = xDiff / meanheight;
//相距距离与灯条长度比值
float ratio = dis / meanheight;
//???
float heightDiff_ratio = heightDiff / max(left.size.height, right.size.height);
//筛选
if (angleDiff > 10 || xDiffRatio < 0.5 || yDiffRatio>0.7 || ratio > 3 || ratio < 1)
continue;
armor_rect.center.x = (left.center.x + right.center.x) / 2;
armor_rect.center.y = (left.center.y + right.center.y) / 2;
armor_rect.angle = (left.angle + right.angle) / 2;
if (180 - angleDiff < 3)
armor_rect.angle += 90;
armor_rect.size.height = (left.size.height + right.size.height) / 2;
armor_rect.size.width = sqrt((left.center.x - right.center.x) * (left.center.x - right.center.x) + (left.center.y - right.center.y) * (left.center.y - right.center.y));
//armor_rect.size.height = armor_rect.size.height * 1.5;
//armor_rect.size.width = armor_rect.size.width * 2;
double nL = armor_rect.size.height;
double nW = armor_rect.size.width;
if (nL < nW)
{
armor_rect.size.height = nL;
armor_rect.size.width = nW;
}
else
{
armor_rect.size.height = nW;
armor_rect.size.width = nL;
}
for (int i = 0; i < armors.size(); i++)
{
Rect roiRect = armors[i].boundingRect();
Mat roiImg = src(roiRect);
imshow("wu", roiImg);
waitKey(0);
}
armor_rects.emplace_back(armor_rect);
armors.push_back(armor_rect);
}
if (armor_rects.empty())
cout << "There is no armor in quality!" << endl;
}
#ifndef DEBUG_SHOW
drawall(armors, src);
imshow("HSV图像", src);
waitKey(1);
#endif // !DEBUG_SHOW
}