《零基础入门RM视觉》装甲板识别详解

写在前面的话:

算是在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

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值