对一张静态图片的识别

首先请出我们的主角

对一张静态图片的识别
我们今天要识别的就是这张图片,然后首先我们来看看最后的识别效果
对一张静态图片的识别

虽然识别出来之后画的标记不是很好,但是只要位置信息出来了,画标记其实是次要的了。

预备知识

这里先介绍一下用到的OpenCV中的名词(有函数有类)

Mat:类名,用于储存图像

namedWindow:使imshow函数展示的图片窗口大小可调

imshow:展示图片,并自定义图片窗口名

RotatedRect:旋转矩形,类名

Point2f:一个点,点的两个坐标为float型变量

points:返回旋转矩形的四个顶点

line:连线函数,参数的意义为:绘制的图片,点1,点2,线的颜色,线的宽度

cvtColor:将图片转化为灰度图,三个参数的含义为:输入图片,输出图片,转换的细节(我也刚学,建议csdn)

threshold:将图片转化为灰度图:五个参数的含义为:输入图片,输出图片,亮度下界,亮度上界(上下界之中的像素会被置为白色,其余为黑色),转化细节(不了解,同上^ 3^)

split:人如其名,将一张图片的RGB分裂开来,以BGR的顺序存在Mat数组中(便于后面的寻找颜色位置)

subtract:做减法,不过对象为两张图片,三个参数为:被减数,减数,差
不过这里需要注意的是,RoboMaster里面只有两种装甲板,红色和蓝色,红色我们就红减蓝,蓝色我们就蓝减红

getStructuringElement:的意义是创建一个用于膨胀操作的图形,两个参数的含义是:图形类型(有矩形,椭圆啥的),大小

dilate:膨胀操作,三个参数的含义为:输入图像,输出图像,膨胀用的图形

Point:表示一个点

findContours:根据二值图片,寻找二值位置的轮廓,四个参数的含义为:输入图片,储存轮廓容器,后面两个是找轮廓的选项(我也不是很了解)

auto:这个不用多说哈

minAreaRect:找到能够装下这个轮廓的最小矩形

waitKey:设置展示图片多久自动关闭窗口,单位为毫秒,若为0,则为手动关闭

然后我们一步步介绍识别步骤

(所有实现基于VS2017+OpenCV4.1.1

首先我们需要读入图片,用库函数imread读入,我们的图片文件需要与main文件在同一位置,不然我这样读取会报错

然后对其进行预处理(传入pretreat函数中),预处理的目的是得到一张经过膨胀操作后的二值图,再用后续的操作得到轮廓并画出

现在我们先介绍pretreat函数:

首先我们将传入的图片转换为一个灰度图
cvtColor(raw, grey, COLOR_BGR2GRAY);
对一张静态图片的识别

然后转化为二值图
threshold(grey, bigrey, 150, 255, THRESH_BINARY);
对一张静态图片的识别

再将原图RGB分离
split(raw, channel);

将B通道减去R通道
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);
对一张静态图片的识别

ret = sub_br & bigrey;这一句,我们用原图 灰度二值 之后的图片与(&)上原图 通道相减二值膨胀 之后的图片,提取出较为可信的候选区域后,再进行膨胀
对一张静态图片的识别

创建另一个用于膨胀的图片
Mat element2 = getStructuringElement(MORPH_RECT, Size(2, 2));

对我们与操作之后的结果进行膨胀操作
dilate(ret, ret, element2);
对一张静态图片的识别

然后将ret作为结果传出

可能会很疑惑,这些参数都是怎么来的
没错都是一个个试出来的,换了图片就不一定管用了



然后介绍一下fixarmor函数(后续的展示基于原图)

其实这张图经过预处理之后还会剩余很多的不需要的白色色块,这个时候就需要我们用匹配进行取舍,只有能够配对的我们才将其保留下来
还有就是,这里的匹配操作的时间复杂度是 O(n2),我说实话我不知道有没有更好的匹配方法

contour:我们储存轮廓的容器,但是我不知道为什么需要这么创建(指模板参数)考ccf差点坑死我(指创建方式)

rect:存我们匹配后的所有矩形

ret:最后配对成功时,会以pair的形式存在ret中

findContours(img, contour, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);找轮廓

然后将轮廓弄成矩形后,存在rect
对一张静态图片的识别
(这里用找到的temp画的图)

然后双重for,匹配灯条(匹配完之后就是我们最后的结果,所以不展示了)
我这里用了角度,面积,长宽比进行筛选
(除了长宽比需要用取出的长除取出的宽之外,其余两个都是成员变量)

后面就是匹配,没错参数也是我一个个试出来的

最后是源码:(这个没有分文件编写

#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;
}

然后按道理是可以将蓝减红改成红减蓝来识别红色灯条的我没试过,如果识别不了,可以评论私信找我击剑

截中间图好麻烦
不要问我为什么用绿色画图,我做题看不到绿色,所以我要多看看绿色

上一篇:oracle 11g rac修改监听端口


下一篇:Redis学习笔记——Redis的发布和订阅