前言

在RM2019赛季结束后的日子,学习了OpenCV和一点C++语言,在2019年国庆节期间初步完成了RM装甲板的识别程序。由于我是初学者,而且当时未看过队内程序,所以写的程序较为简单易懂,程序输出最佳装甲板的二维坐标。

识别方法

识别方法

程序

  • main.cpp
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>
#include <iostream>
#include "armordetection.h"

using namespace cv;
using namespace std;

ArmorDetection* armor = new ArmorDetection();
Point2f center;

int main()
{
	//fps变量
	double t = (double)getTickCount();
	double fps;
	char string[10];
	char string2[10];
	Mat frame;

	VideoCapture capture("C:/Users/Administrator/Desktop/video/6.mp4");
	if (!capture.isOpened())
	{
		printf("无法打开相机...\n");
		return -1;
	}

	namedWindow("frame", CV_WINDOW_AUTOSIZE);
	namedWindow("mask", CV_WINDOW_AUTOSIZE);
	namedWindow("Control", CV_WINDOW_AUTOSIZE);

	while (capture.read(frame))//读取当前帧
	{
		armor->setInputImage(frame);
		armor->Pretreatment();
		center = armor->GetArmorCenter();
		cout << "[INFO] x = " << center.x - frame.cols / 2 << "    y = " << center.y - frame.rows / 2 << endl;

		//计算fps
		double dt = ((double)getTickCount() - t) / (double)getTickFrequency();
		fps = 1.0 / dt;
		t = (double)getTickCount();
		sprintf_s(string, "%.2f", fps);
		std::string fpsString("FPS:");
		fpsString += string;
		putText(frame, fpsString, Point(5, 20), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 255, 255));

		char c = waitKey(10);
		if (c == 27) //"Esc"
		{
			break;
		}
	}
	capture.release();//释放视频内存
	waitKey(0);
	return 0;
}
  • armordetection.cpp
#include "armordetection.h"

ArmorDetection::ArmorDetection() = default;

ArmorDetection::ArmorDetection(Mat & input) {
	frame = input;
}

void ArmorDetection::setInputImage(Mat input) {
	frame = input;
	currentCenter.x = 0;
	currentCenter.y = 0;
}

//图像预处理
void ArmorDetection::Pretreatment() {
	Mat input;
	Point p, center;
	vector<vector<Point>> contours;
	vector<Vec4i> hireachy;
	vector<Rect> boundRect(contours.size());
	Point2f vertex[4];

	//创建进度条
	cvCreateTrackbar("LowH", "Control", &iLowH, 255);
	cvCreateTrackbar("HighH", "Control", &iHighH, 255);

	cvCreateTrackbar("LowS", "Control", &iLowS, 255);
	cvCreateTrackbar("HighS", "Control", &iHighS, 255);

	cvCreateTrackbar("LowV", "Control", &iLowV, 255);
	cvCreateTrackbar("HighV", "Control", &iHighV, 255);

	cvtColor(frame, hsv, CV_BGR2HSV);
	inRange(hsv,
		Scalar(iLowH, iLowS, iLowV),
		Scalar(iHighH, iHighS, iHighV),
		mask);
	// 形态学操作
	morphologyEx(mask, mask, MORPH_OPEN, kernel1, Point(-1, -1));//开操作
	dilate(mask, mask, kernel2, Point(-1, -1), 1);//膨胀
	//轮廓增强
	Canny(mask, mask, 3, 9, 3);
	imshow("mask", mask);
	findContours(mask, contours, hireachy, CV_RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

	//筛选,去除一部分矩形
	for (int i = 0; i < contours.size(); ++i) {
		RotatedRect minRect = minAreaRect(Mat(contours[i]));
		minRect.points(vertex);
		if (minRect.size.width > minRect.size.height) {
			minRect.angle += 90;
			float t = minRect.size.width;
			minRect.size.width = minRect.size.height;
			minRect.size.height = t;
		}

		if ((minRect.size.width * 10 > minRect.size.height)
			&& (minRect.size.width * 1 < minRect.size.height)
			&& (abs(minRect.angle) < 30)) {
			minRects.push_back(minRect);
		}
		for (int l = 0; l < 4; l++)
		{
			line(frame, vertex[l], vertex[(l + 1) % 4], Scalar(255, 0, 0), 2);
		}
		line(frame, Point(frame.cols / 2 - 15, frame.rows / 2),
			Point(frame.cols / 2 + 15, frame.rows / 2), Scalar(0, 255, 255), 5);
		line(frame, Point(frame.cols / 2, frame.rows / 2 - 15),
			Point(frame.cols / 2, frame.rows / 2 + 15), Scalar(0, 255, 255), 5);
		circle(frame, Point(frame.cols / 2, frame.rows / 2), 4, Scalar(0, 0, 255), -1);
	}
}


Point2f ArmorDetection::GetArmorCenter() {
	//遍历所有矩形,两两组合
	RotatedRect leftRect, rightRect;
	vector<int*> reliability;
	double area[2], distance, height;

	if (minRects.size() < 2) {
		LostTarget();
		return currentCenter;
	}

	for (int i = 0; i < minRects.size(); ++i) {
		for (int j = i + 1; j < minRects.size(); ++j) {
			int level = 0;
			int temp[3];
			leftRect = minRects[i];
			rightRect = minRects[j];

			//判断
			if (leftRect.angle == rightRect.angle) {
				level += 10;
			}
			else if (abs(leftRect.angle - rightRect.angle) < 5) {
				level += 8;
			}
			else if (abs(leftRect.angle - rightRect.angle) < 10) {
				level += 6;
			}
			else if (abs(leftRect.angle - rightRect.angle) < 30) {
				level += 4;
			}
			else if (abs(leftRect.angle - rightRect.angle) < 90) {
				level += 1;
			}
			else {
				break;
			}

			area[0] = leftRect.size.width * leftRect.size.height;
			area[1] = rightRect.size.width * rightRect.size.height;
			if (area[0] == area[1]) {
				level += 10;
			}
			else if (min(area[0], area[1]) * 1.5 > max(area[0], area[1])) {
				level += 8;
			}
			else if (min(area[0], area[1]) * 2 > max(area[0], area[1])) {
				level += 6;
			}
			else if (min(area[0], area[1]) * 3 > max(area[0], area[1])) {
				level += 4;
			}
			else if (min(area[0], area[1]) * 4 > max(area[0], area[1])) {
				level += 1;
			}
			else {
				break;
			}

			double half_height = (leftRect.size.height + rightRect.size.height) / 4;
			if (leftRect.center.y == rightRect.center.y) {
				level += 10;
			}
			else if (abs(leftRect.center.y - rightRect.center.y) < 0.2 * half_height) {
				level += 8;
			}
			else if (abs(leftRect.center.y - rightRect.center.y) < 0.4 * half_height) {
				level += 6;
			}
			else if (abs(leftRect.center.y - rightRect.center.y) < 0.8 * half_height) {
				level += 4;
			}
			else if (abs(leftRect.center.y - rightRect.center.y) < half_height) {
				level += 1;
			}
			else {
				break;
			}

			distance = Distance(leftRect.center, rightRect.center);
			height = (leftRect.size.height + rightRect.size.height) / 2;
			if (distance != 0 && distance > height) {
				if (distance < 1.5 * height) {
					level += 6;
				}
				else if (distance < 1.8 * height) {
					level += 4;
				}
				else if (distance < 2.4 * height) {
					level += 2;
				}
				else if (distance < 10 * height) {
					level += 1;
				}
				else {
					break;
				}
			}

			temp[0] = i;
			temp[1] = j;
			temp[2] = level;

			reliability.push_back(temp);

		}
	}

	if (reliability.empty()) {
		LostTarget();
		return currentCenter;
	}
	else {

		int maxLevel = 0, index = 0;
		for (int k = 0; k < reliability.size(); ++k) {
			if (reliability[k][2] > maxLevel) {
				maxLevel = reliability[k][2];
				index = k;
			}
		}

		currentCenter.x = (minRects[reliability[index][0]].center.x + minRects[reliability[index][1]].center.x) / 2;
		currentCenter.y = (minRects[reliability[index][0]].center.y + minRects[reliability[index][1]].center.y) / 2;

		//与上一次的结果对比
		if (lastCenter.x == 0 && lastCenter.y == 0) {
			lastCenter = currentCenter;
			lost = 0;
		}
		else {
			double difference = Distance(currentCenter, lastCenter);
			if (difference > 300) {
				LostTarget();
				return currentCenter;
			}
		}
		line(frame, Point(currentCenter.x - 10, currentCenter.y - 10),
			Point(currentCenter.x + 10, currentCenter.y + 10), Scalar(255, 255, 0), 5);
		line(frame, Point(currentCenter.x + 10, currentCenter.y - 10),
			Point(currentCenter.x - 10, currentCenter.y + 10), Scalar(255, 255, 0), 5);
		circle(frame, currentCenter, 7.5, Scalar(0, 0, 255), 5);
		imshow("frame", frame);
		return currentCenter;
	}
}

void ArmorDetection::LostTarget() {
	lost++;
	if (lost < 3) {
		currentCenter = lastCenter;
	}
	else {
		currentCenter = Point2f(0, 0);
		lastCenter = Point2f(0, 0);
	}
}

double ArmorDetection::Distance(Point2f a, Point2f b) {
	return sqrt((a.x - b.x) * (a.x - b.x) +
		(a.y - b.y) * (a.y - b.y));
}

double ArmorDetection::max(double first, double second) {
	return first > second ? first : second;
}

double ArmorDetection::min(double first, double second) {
	return first < second ? first : second;
}
  • armordetection.h
#pragma once
#ifndef ARMORDETECTION_H
#define ARMORDETECTION_H

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

using namespace std;
using namespace cv;

class ArmorDetection {
private:
	Mat frame, hsv, mask;
	Mat kernel1 = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	Mat kernel2 = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
	Point2f currentCenter;
	Point2f lastCenter;
	vector<RotatedRect> minRects;
	int lost;

public:
	ArmorDetection();
	explicit ArmorDetection(Mat& input);
	void setInputImage(Mat input);
	void Pretreatment();
	Point2f GetArmorCenter();
	
	int iLowH = 49;
	int iHighH = 93;

	int iLowS = 5;
	int iHighS = 95;

	int iLowV = 255;
	int iHighV = 255;

private:
	void LostTarget();
	double Distance(Point2f, Point2f);
	double max(double, double);
	double min(double, double);
};


#endif 

识别效果

识别效果

结语

该程序存在很多瑕疵,仅供初学者参考:)
(小车儿挺帅~据说远处的1号更帅^v*)