装甲板识别个人实现

Posted iacorse

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了装甲板识别个人实现相关的知识,希望对你有一定的参考价值。

首先是main函数

#include <iostream>
#include <opencv2/opencv.hpp>
#include "ArmorPlate.h"

using namespace std;
using namespace cv;

bool CameraRead(ArmorPlate& armor_param);

int main()

    ArmorPlate armor;

    armor.CameraInit(0);

    //while (1)
    //
        
        //if (!CameraRead(armor))
            //continue;
    
        armor.AutoShoot();

    //
    return 0;


bool CameraRead(ArmorPlate& armor_param)

    armor_param.capture_plate.read(armor_param.armor_image);
    if (!armor_param.armor_image.data)
    
        cout << "The camera has not read a image!" << endl;
        armor_param.CameraInit(0);
        return false;
    
    else
        return true;

之后是头文件:

#pragma once

#define BLUETEAM 0
#define REDTEAM 1

class ArmorPlate

public:
    cv::Mat pre_image;
    cv::Mat armor_image;
    cv::VideoCapture capture_plate;

    int myteam;


    ArmorPlate();
    bool CameraInit(int device);
    void AutoShoot();

private:
    void ImgPreprosses(const cv::Mat& src, const cv::Mat& dst);


;


class ArmorRect

public:
    cv::RotatedRect armors;
;

之后就是CPP文件:

#include <opencv2/opencv.hpp>
#include "ArmorPlate.h"

using namespace std;
using namespace cv;


ArmorPlate::ArmorPlate()

    myteam = REDTEAM;


bool ArmorPlate::CameraInit(int device)

    capture_plate.open(device);
    if (!capture_plate.isOpened())
    
        cout << "The capture has something wrong!";
        return false;
    
    else return true;


cv::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;


void ArmorPlate::AutoShoot()

    armor_image = imread("2.jpg");
    ImgPreprosses(armor_image, pre_image);
    imshow("原图", armor_image);
    //imshow("预处理图", pre_image);
    waitKey(0);




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);
    




void ArmorPlate::ImgPreprosses(const Mat& src, const Mat& dst)

    Mat grayImg;
    Mat binBrightImg;
    vector<RotatedRect> lightInfos;
    double MaxValue;
    vector<Mat> channels;
    //split(src, channels);
    //if (myteam == REDTEAM)
    //
    //  grayImg = channels.at(0)- channels.at(2);
    //  blur(grayImg, grayImg, Size(3, 3));
    //  //grayImg *= 3;
    //
    //else
    //  grayImg = channels.at(2) - channels.at(0);
    //imshow("灰度图",grayImg);
    //minMaxLoc(grayImg, 0, &MaxValue, 0, 0);waitKey(0);
    //threshold(grayImg, binBrightImg, MaxValue*0.85, 255, THRESH_BINARY);//THRESH_BINARY
    ////CV_THRESH_OTSU不可用,因为该方法用于区分前景和后景

    //Mat element = getStructuringElement(MORPH_RECT,Size(3,3));

    //morphologyEx(binBrightImg, binBrightImg, MORPH_DILATE, element, Point(-1, -1), 1);
    ////dilate(binBrightImg, binBrightImg, element);
    //morphologyEx(binBrightImg, binBrightImg, MORPH_OPEN, element,Point(-1,-1),3);
    ////morphologyEx(binBrightImg, binBrightImg, MORPH_DILATE, element, Point(-1, -1), 1);
    //morphologyEx(binBrightImg, binBrightImg, MORPH_CLOSE, element, Point(-1, -1), 3);
    ////medianBlur(binBrightImg, binBrightImg, 11);
    ////dilate(binBrightImg, binBrightImg, element);
    //imshow("二值图", binBrightImg);waitKey(0);

    //方法2//
    Mat HSVImg;
    Mat image;
    cvtColor(src, HSVImg, COLOR_BGR2HSV);
    split(HSVImg, channels);
    minMaxLoc(channels[2], 0, &MaxValue, 0, 0); 
    threshold(channels[2], channels[2], MaxValue*0.98, 255, THRESH_BINARY);
    Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
    medianBlur(channels[2], channels[2], 3);
    morphologyEx(channels[2], channels[2], MORPH_DILATE, element, Point(-1, -1), 1);
    imshow("V通道二值图", channels[2]); waitKey();
    HSVImg.copyTo(image, channels[2]);
    int BLowH = 80;
    int BHighH = 150;
    int BLowS = 60;
    int BHighS = 255;
    int BLowV = 100;
    int BHighV = 255;
    //inRange(image, Scalar(BLowH, BLowS, BLowV), Scalar(BHighH, BHighS, BHighV), binBrightImg);
    //imshow("二值图", binBrightImg); waitKey(0);
    binBrightImg = channels[2];
    //方法2结束//

    vector<vector<Point>> lightContours;
    cv::findContours(binBrightImg.clone(), lightContours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    Mat drawingImg = Mat::zeros(src.size(), CV_8UC3);
    for (int i = 0; i < lightContours.size(); i++)
        drawContours(drawingImg, lightContours, i, Scalar(0, 255, 0));
    imshow("轮廓图", drawingImg); waitKey(0);

    lightInfos.clear();

    for (const auto& contour : lightContours)
    
        float lightContourArea = contourArea(contour);
        if (contour.size() <= 5 ||lightContourArea <10 ) continue;

        

        RotatedRect lightRec = fitEllipse(contour);
        RotatedRect minAreaRec = minAreaRect(contour);
        adjustRec(lightRec);
        if ((lightRec.size.width / lightRec.size.height) >0.8)
            continue;
        int x = lightRec.center.x - lightRec.size.width;
        if (x < 0)
            continue;
        int y = lightRec.center.y - lightRec.size.height;
        if (y < 0)
            continue;
        //cout << lightRec.angle << endl;
        //RotatedRect s;
        //s.angle = lightRec.angle;
        //s.center = lightRec.center;
        //if (minAreaRec.size.width > minAreaRec.size.height)
        //
        //  s.size.height = minAreaRec.size.width;
        //  s.size.width = minAreaRec.size.height;
        //  //lightRec.angle += 90;
        //
        //else
        //
        //  s.size.height = minAreaRec.size.height;
        //  s.size.width = minAreaRec.size.width;
        //
        //
        //
        //if (lightRec.size.width > lightRec.size.height)
        //
        //  swap(lightRec.size.width, lightRec.size.height);
        //

        if (lightRec.size.width / lightRec.size.height > 1.0 ||
            lightContourArea / lightRec.size.area() < 0.5)
            continue;
        lightRec.size.width *= 1.1;
        lightRec.size.height *= 1.1;

        Rect boundRect = lightRec.boundingRect();
        Mat lightImg = src(boundRect);

        if((lightRec.size.height>10&& (lightRec.size.height < 150)&&(lightRec.angle<45||lightRec.angle>135)))
            lightInfos.push_back(lightRec);


    

    vector<RotatedRect> armors;
    vector<ArmorRect> armorRects;
    ArmorRect armorRect;

    armors.clear();
    armorRects.clear();

    if (lightInfos.size()<=1)
    
        cout << "There's no light contours in quality." << endl;
    
    
    sort(lightInfos.begin(), lightInfos.end(), [](const RotatedRect& ld1, const RotatedRect& ld2)
    
        return ld1.center.x < ld2.center.x;
    );

    for (int i = 0; i < lightInfos.size(); i++)
    
        for (int j = i + 1; j < lightInfos.size(); j++)
        
            const RotatedRect& left = lightInfos[i];
            const RotatedRect& right = lightInfos[j];

            double heightDiff = abs(left.size.height - right.size.height);
            double widthDiff = abs(left.size.width - right.size.width);
            double angleDiff = abs(left.angle - right.angle);
            double yDiff = abs(left.center.y - right.center.y);
            double xDiff = abs(left.center.x - right.center.x);
            double meanheight = (left.size.height + right.size.height)/2;
            double yDiffRatio = yDiff / meanheight;
            double xDiffRatio = xDiff / meanheight;
            double 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));
            double 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;

            armorRect.armors.center.x = (left.center.x + right.center.x) / 2;
            armorRect.armors.center.y = (left.center.y + right.center.y) / 2;
            armorRect.armors.angle= (left.angle + right.angle) / 2;
            //cout << left.angle << endl;
            //armorRect.armors.angle = 0;
            if (180 - angleDiff < 3)
                armorRect.armors.angle += 90;
            armorRect.armors.size.height= (left.size.height + right.size.height) / 2;
            armorRect.armors.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));

            double nL = armorRect.armors.size.height;
            double nW = armorRect.armors.size.width;
            if (nL < nW)
            
                armorRect.armors.size.height = nL;
                armorRect.armors.size.width = nW;
            
            else
            
                armorRect.armors.size.height = nW;
                armorRect.armors.size.width = nL;
            

            armorRects.emplace_back(armorRect);
            armors.push_back(armorRect.armors);
        
    
    if (armorRects.empty())
        cout << "There is no armor in quality!" << endl;
    
    drawall(armors, src);
    imshow("", src);

本来一开始在预处理时用的是RGB通道(注释部分),通过RB通道相减(或者带权重的RGB相减),但是效果不好:

技术图片

技术图片

技术图片

技术图片

由于预处理去噪填坑将255区的长宽差不够,受蓝色灯光的影响导致椭圆拟合的角度总是不够好,提高阈值和后面调参的效果也不佳。在最后换成了HSV通道的处理,最后在经过一系列的调参工作后,总算得到了还算不错的效果:

技术图片

技术图片

技术图片

技术图片

以上是关于装甲板识别个人实现的主要内容,如果未能解决你的问题,请参考以下文章

东南大学RM装甲板识别算法详解

了解RoboMaster视觉组视觉在各兵种中的作用

基于C++的车辆装甲板检测自瞄系统

了解RoboMaster视觉组

openFace 人脸识别框架测试

利用微软认知服务实现语音识别功能