Recognition of a static picture

First of all, please show our protagonist


What we want to identify today is this picture. First, let's look at the final recognition effect:

Although the marks drawn after recognition are not very good, as long as the location information comes out, the marks drawn are actually secondary.

Preparatory knowledge

Here we first introduce the nouns used in OpenCV (there are functions and classes)

Mat: class name, used to store images

namedWindow: make the size of the picture window displayed by imshow function adjustable

imshow: display pictures and customize the picture window name

RotatedRect: rotating rectangle, class name

Point2f: a point whose two coordinates are float variables

points: returns the four vertices of the rotated rectangle

Line: line function. The meaning of parameters is: drawn picture, point 1, point 2, line color and line width

cvtColor: converts an image into a grayscale image. The meanings of the three parameters are: input image, output image, and conversion details (I just learned, and suggest csdn)

threshold: convert a picture into a grayscale image: the meaning of the five parameters is: input picture, output picture, brightness lower bound, brightness upper bound (the pixels in the upper and lower bounds will be set to white, and the rest will be black), and conversion details (I don't know, ditto ^ 3 ^)

split: a person splits the RGB of a picture according to his name and stores it in the Mat array in BGR order (for finding the color position later)

subtract: perform subtraction, but the object is two pictures. The three parameters are: subtracted, subtracted and difference
However, it should be noted that there are only two kinds of armor plates in the RoboMaster, red and blue. Red reduces blue, and blue reduces red

getStructuringElement: the meaning is to create a graph for expansion operation. The meaning of the two parameters is: graph type (rectangle, ellipse, etc.) and size

dilate: expansion operation. The meanings of the three parameters are: input image, output image and graphics for expansion

Point: represents a point

Findcontour: find the contour of the binary position according to the binary image. The meaning of the four parameters is: enter the image, store the contour container, and the last two are the options to find the contour (I don't know very well)

auto: Needless to say

Minarea rect: find the smallest rectangle that can hold this contour

waitKey: sets how long the display image automatically closes the window, in milliseconds. If it is 0, it is closed manually

Then we introduce the recognition steps step by step

(all implementations are based on VS2017+OpenCV4.1.1)

First of all, we need to read in the picture with the library function imread. Our picture file needs to be in the same location as the main file, otherwise I will report an error if I read it like this

Then it is preprocessed (passed into the pretreat function). The purpose of preprocessing is to obtain a binary graph after expansion operation, and then the contour is obtained and drawn by subsequent operations

Now let's introduce the pretreat function:

First, we convert the incoming image into a grayscale image
cvtColor(raw, grey, COLOR_BGR2GRAY);

Then it is transformed into a binary graph
threshold(grey, bigrey, 150, 255, THRESH_BINARY);

Then separate the original RGB
split(raw, channel);

Subtract channel R from channel B
subtract(channel[0], channel[2], sub_br);

Binarize the subtracted picture
threshold(sub_br, sub_br, 110, 255, THRESH_BINARY);

Create a shape for inflation
Mat element1 = getStructuringElement(MORPH_RECT, Size(3, 3));

Perform the expansion operation
dilate(sub_br, sub_br, element1);

ret = sub_ br & bigrey; In this sentence, we use the image after the gray binary value of the original image and the image after the binary expansion of the original image channel on (&) to extract a more reliable candidate region, and then expand it

Create another picture for expansion
Mat element2 = getStructuringElement(MORPH_RECT, Size(2, 2));

Expand the results after our operation
dilate(ret, ret, element2);

Then ret is sent out as a result

You may wonder how these parameters come from
Yes, they all try out one by one. Changing pictures may not work

Then, let's introduce the fixrmor function (the subsequent display is based on the original figure)

In fact, after preprocessing, there will be a lot of unnecessary white color blocks left in this picture. At this time, we need to use matching to make choices. Only those that can be matched can be retained
Also, the time complexity of the matching operation here is O(n2). To be honest, I don't know if there is a better matching method

contour: we are a container for storing contours, but I don't know why it needs to be created like this (referring to template parameters). ccf almost killed me (referring to the creation method)

rect: save all our matched rectangles

ret: when the last pairing is successful, it will be stored in ret in the form of pair

findContours(img, contour, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); Find contour

Then, after the contour is made into a rectangle, it is stored in rect

(here is the diagram drawn with the found temp)

Then double for and match the light bar (our final result is after matching, so we won't show it)
I used angle, area and aspect ratio for screening
(except for the length to width ratio, the other two are member variables except the length to be extracted and the width to be extracted)

Then there is matching. Yes, I tried out the parameters one by one

Finally, the source code: (this is not written in separate files

#include<opencv2/opencv.hpp>
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
using namespace cv;

void show(Mat img, string name)
{
	namedWindow(name, 0);
	imshow(name, img);
	return;
}

void drawrect(Mat &img, RotatedRect ob)
{
	Point2f v[4];
	ob.points(v);
	for (int i = 0; i < 4; i++)
		line(img, v[i], v[(i + 1) % 4], Scalar(0, 255, 0), 1);
	return;
}

void drawtarget(Mat &img, RotatedRect ob1, RotatedRect ob2)
{
	Point2f v1[4], v2[4];
	ob1.points(v1);
	ob2.points(v2);
	line(img, v1[0], v1[1], Scalar(0, 255, 0), 1);
	line(img, v1[0], v2[3], Scalar(0, 255, 0), 1);
	line(img, v2[2], v1[1], Scalar(0, 255, 0), 1);
	line(img, v2[2], v2[3], Scalar(0, 255, 0), 1);
	return;
}

Mat pretreat(Mat raw)
{
	Mat grey;
	cvtColor(raw, grey, COLOR_BGR2GRAY);
	Mat bigrey;
	threshold(grey, bigrey, 150, 255, THRESH_BINARY);
	Mat channel[3];
	split(raw, channel);
	Mat sub_br;
	subtract(channel[0], channel[2], sub_br);
	threshold(sub_br, sub_br, 110, 255, THRESH_BINARY);
	Mat element1 = getStructuringElement(MORPH_RECT, Size(3, 3));
	dilate(sub_br, sub_br, element1);
	Mat ret;
	ret = sub_br & bigrey;
	Mat element2 = getStructuringElement(MORPH_RECT, Size(2, 2));
	dilate(ret, ret, element2);
	return ret;
}

void fixarmor(Mat &raw,Mat img)
{
	vector<vector<Point>> contour;
	vector<RotatedRect> rect,finall;
	vector<pair<RotatedRect, RotatedRect>> ret;
	findContours(img, contour, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	for (auto x : contour)
	{
		RotatedRect temp = minAreaRect(x);
		rect.push_back(temp);
	}
	for (auto x : rect)
	{
		for (auto y : rect)
		{
			double xangle, yangle, xarea, yarea, xrate, yrate;
			xarea = x.size.area();
			xangle = x.angle;
			yarea = y.size.area();
			yangle = y.angle;
			xrate = x.size.height / x.size.width;
			yrate = y.size.height / y.size.width;
			if (xarea == yarea) continue;
			if (xangle > 10 || yangle > 10) continue;
			if (xarea < 15 || yarea < 15) continue;
			if (fabs(xarea - yarea) < 20 && fabs(xangle - yangle) < 2
				&& fabs(xrate - yrate) < 0.1&&fabs(x.size.height - y.size.height) < 5
				&& fabs(x.size.width - y.size.width) < 5)
				ret.push_back({ x,y });
		}
	}
	//cout << ret.size() << endl;
	for (auto x : ret) { drawrect(raw, x.first); drawrect(raw, x.second);}
	for (auto x : ret) drawtarget(raw, x.first, x.second);
	return;
}

int main()
{
	Mat hero = imread("hero.jpg");
	Mat temp = pretreat(hero);
	fixarmor(hero,temp);
	show(hero, "hero");
	waitKey(0);
	//system("pause");
	return 0;
}

Then, according to the truth, you can change the blue minus red to red minus blue to identify the red light bar. I haven't tried. If you can't identify it, you can comment on the private letter and ask me for fencing

It's troublesome to cut the middle picture
Don't ask me why I draw with green. I can't see green when I do questions, so I want to see more green

Tags: AI OpenCV Deep Learning RoboMaster

Posted by Black Rider on Wed, 22 Sep 2021 05:09:09 +0530