OpenCV4深度神经网络DNN实战教程

OpenCV4深度神经网络DNN实战教程

1-概述与环境配置

OpenCV DNN模块

传统的HAAR级联人脸检测器不可靠到DNN的高可靠

各种操作

,如何将TensorFlow训练出来的模型导入到,实现很好的调用

OpenCVDNN模块可以解决

OpenCV4不再是单纯的计算机视觉库,还有深度学习推理库

OpenCV4后与神经网络

扩展模块

在3.3中放到了release版本中

旧的教程下线有瑕疵

此时已经很成熟了,通过IE加速(推理引擎,7、8倍的效率提升)

OpenCV4模型数很多

OpenCVgithub官方网站https://github.com/opencv/opencv

点击wiki

https://github.com/opencv/opencv/wiki/Deep-Learning-in-OpenCV

有很多关于深度学习的介绍

OpenCV face detector已经做的非常好了,模型只有几兆,但检测,完全可以抛弃级联检测方式

整个目录围绕模型展开。

色彩,风格迁移

OpenCV 的DNN如何使用,在应用层


2-卷积神经网络概述

如何还用OpenCV的API加载神经网络各个层次

卷积神经网络框架中,有paddle填充模式,sim是输入3232大小,输出还是32 32大小。而value输入3232,输出则变为2828.。

重叠池化的步长永远是1

局部池化,步长是其大小

最大值池化、均值池化,根据数据

3-加载网络模型与设置

https://www.pianshen.com/article/9895277588/

如何加载一个网络,网络有输入,输入是图像,c通道数,h高度,w宽度,N一般为1,表示1张图像的预测,也可以多张图像

获取输出层有个api

3.1 所需的模型下载

opencv_tutorial-master文件是从Google下载的,下载路径为实际上下载的东西在贾老师的GitHub上就有,

https://github.com/gloomyfish1998/opencv_tutorial/

将下载好的文件保存在了D:\OpenCV\project\下

3.2 所使用的模型(googlenet)

D:\OpenCV\project\opencv_tutorial-master\data\models\googlenet

bvlc_googlenet.caffemodel这是个caffe模型,在OpenCV中支持离线加载,不依赖caffe,这是模型的权重文件

bvlc_googlenet.prototxt这是模型的描述文件

Imagenet支持1000个分类,分类类别在classification_classes_ILSVRC2012.txt可以看到,使用editplus可以看到一直到1000行

//有了模型的权重和描述文件就可以加载模型了

3.3 Net介绍

(1)在OpenCV中加载模型要通过readNetWork函数

(2)readNet函数有三个参数,参数1加载网络(caffe、TensorFlow等),参数2,3为权重路径和描述文件

(3)或者readNetFromCaffe(TensorFlow、darknet、ONNX等),意思为读进来的必须是caffe模型,此外还支持TensorFlow等模型,只有两个参数

(4)还有readNetFromModelOptimizer从dnn模型优化器中读取模型优化后的模型

Net net = readNetFromCaffe(protxt, bin_model);	//此处只能加载caffe的

3.4 代码输出网络结构信息

#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>			//包含dnn模块的头文件
#include <iostream>

using namespace cv;
using namespace cv::dnn;			//包含dnn的命名空间
using namespace std;

int main() {

	string bin_model = "D:/OpenCV/project/opencv_tutorial-master/data/models/googlenet/bvlc_googlenet.caffemodel";		//定义模型权重文件的加载路径
	string protxt = "D:/OpenCV/project/opencv_tutorial-master/data/models/googlenet/bvlc_googlenet.prototxt";			//定义模型描述文件的加载路径
	//Imagenet支持1000个分类,分类类别在classification_classes_ILSVRC2012.txt可以看到,一直到1000

	//有了模型的权重和描述文件就可以加载模型了
	//在OpenCV中加载模型要通过readNetWork,
	//readNet函数有三个参数,参数1加载网络(caffe、TensorFlow等),参数2,权重路径,参数3,描述文件
	//或者readNetFromCaffe(TensorFlow、darknet、ONNX等),意思为读进来的必须是caffe模型,此外还支持TensorFlow等模型,只有两个参数
	//还有readNetFromModelOptimizer从dnn模型优化器中读取模型优化后的模型
	Net net = readNetFromCaffe(protxt, bin_model);

	vector<string> layer_names = net.getLayerNames();		//此时我们就可以获取所有层的名称了,有了这些可以将其ID取出
	for (int i = 0; i < layer_names.size(); i++) {
		int id = net.getLayerId(layer_names[i]);			//通过name获取其id
		auto layer = net.getLayer(id);						//通过id获取layer
		printf("layer id:%d,type:%s,name:%s\n", id, layer->type.c_str(), layer->name.c_str());	//将每一层的id,类型,姓名打印出来(可以明白此网络有哪些结构信息了)

	}

	waitKey(0);
	return 0;
}


我们知道这个网络有142层,Convolution卷积层,relu激活函数,pooling池化层,此时就搞清楚网络结构了

现在去搞其他的网络的层等信息也就清楚了

3.5 计算后台设置

//设置计算后台(OpenCVdnn模块支持设置不同的计算后台和在不同的设备上进行)
	net.setPreferableBackend(DNN_BACKEND_OPENCV);				//setPreferableBackend实际计算后台,default默认是DNN_BACKEND_OPENCV作为计算后台,使用此就行(也有加速后台ENGINE)
	net.setPreferableTarget(DNN_TARGET_CPU);						//设置在什么设备上进行计算(opencl(需要有interl图形卡)、FPGA、CPU)
	//当上面两个设置之后,他在执行网络进行推算时就会执行此计算后台进行计算(不同的计算后台有不同的效果,速度也有差别)

本节课学习了如何加载一个网络,此网络是Googlenet的,下节会讲如何使用此网络实现图像的分类预测

3.6 所有代码

#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>			//包含dnn模块的头文件
#include <iostream>

using namespace cv;
using namespace cv::dnn;			//包含dnn的命名空间
using namespace std;

int main() {

	string bin_model = "D:/OpenCV/project/opencv_tutorial-master/data/models/googlenet/bvlc_googlenet.caffemodel";		//定义模型权重文件的加载路径
	string protxt = "D:/OpenCV/project/opencv_tutorial-master/data/models/googlenet/bvlc_googlenet.prototxt";			//定义模型描述文件的加载路径
	//Imagenet支持1000个分类,分类类别在classification_classes_ILSVRC2012.txt可以看到,一直到1000

	//有了模型的权重和描述文件就可以加载模型了
	//在OpenCV中加载模型要通过readNetWork,
	//readNet函数有三个参数,参数1加载网络(caffe、TensorFlow等),参数2,权重路径,参数3,描述文件
	//或者readNetFromCaffe(TensorFlow、darknet、ONNX等),意思为读进来的必须是caffe模型,此外还支持TensorFlow等模型,只有两个参数
	//还有readNetFromModelOptimizer从dnn模型优化器中读取模型优化后的模型
	Net net = readNetFromCaffe(protxt, bin_model);

	//设置计算后台(OpenCVdnn模块支持设置不同的计算后台和在不同的设备上进行)
	net.setPreferableBackend(DNN_BACKEND_OPENCV);				//setPreferableBackend实际计算后台,default默认是DNN_BACKEND_OPENCV作为计算后台,使用此就行(也有加速后台ENGINE)
	net.setPreferableTarget(DNN_TARGET_CPU);						//设置在什么设备上进行计算(opencl(需要有interl图形卡)、FPGA、CPU)
	//当上面两个设置之后,他在执行网络进行推算时就会执行此计算后台进行计算(不同的计算后台有不同的效果,速度也有差别)

	//获取各层信息
	vector<string> layer_names = net.getLayerNames();		//此时我们就可以获取所有层的名称了,有了这些可以将其ID取出
	for (int i = 0; i < layer_names.size(); i++) {
		int id = net.getLayerId(layer_names[i]);			//通过name获取其id
		auto layer = net.getLayer(id);						//通过id获取layer
		printf("layer id:%d,type:%s,name:%s\n", id, layer->type.c_str(), layer->name.c_str());	//将每一层的id,类型,姓名打印出来(可以明白此网络有哪些结构信息了)

	}

	waitKey(0);
	return 0;
}

4-图像分类网络inception的使用

池化层降维,卷积层提取特征,

利用已经训练好的模型在OpenCV中进行离线的调用

之前加载caffe模型,各个层次读取

图像分类识别

此图像分类模型主要来自谷歌,在CVPR2015发表

与之前的相比发表了inception模型

此模型可以对1000个类别进行分类

在OpenCV官方网站有关于模型中通道是BGR还是RGB的说明

https://github.com/opencv/opencv/wiki/Deep-Learning-in-OpenCV

https://github.com/opencv/opencv/blob/master/samples/dnn/models.yml

有个配置文件,配置说明文件,就说了各个模型调用的时候有哪些需要输入的参数。通过此模型给出的参数 进行设置

Googlenet是RGB的通道顺序

最后一层是softmax

基于imagenet数据集的

它的类有

D:\OpenCV\project\opencv_tutorial-master\data\models\googlenet中的

classification_classes_ILSVRC2012.txt

构建输入可以参考OpenCV自带的配置文件,根据此文件进行构建输入

D:\OpenCV\opencv-4.4.0-vc14_vc15\opencv\sources\samples\dnn\models.yml

打开文件可以看到,上方链接为模型的下载链接,下方参数为构建输入,进行图像预处理时的参数

当我们需要继续对其分类时

#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>			//包含dnn模块的头文件
#include <iostream>

using namespace cv;
using namespace cv::dnn;			//包含dnn的命名空间
using namespace std;

int main() {

	string bin_model = "D:/OpenCV/project/opencv_tutorial-master/data/models/googlenet/bvlc_googlenet.caffemodel";		//定义模型权重文件的加载路径
	string protxt = "D:/OpenCV/project/opencv_tutorial-master/data/models/googlenet/bvlc_googlenet.prototxt";			//定义模型描述文件的加载路径
	//Imagenet支持1000个分类,分类类别在classification_classes_ILSVRC2012.txt可以看到,一直到1000

	//有了模型的权重和描述文件就可以加载模型了
	//在OpenCV中加载模型要通过readNetWork,
	//readNet函数有三个参数,参数1加载网络(caffe、TensorFlow等),参数2,权重路径,参数3,描述文件
	//或者readNetFromCaffe(TensorFlow、darknet、ONNX等),意思为读进来的必须是caffe模型,此外还支持TensorFlow等模型,只有两个参数
	//还有readNetFromModelOptimizer从dnn模型优化器中读取模型优化后的模型
	Net net = readNetFromCaffe(protxt, bin_model);

	//设置计算后台(OpenCVdnn模块支持设置不同的计算后台和在不同的设备上进行)
	net.setPreferableBackend(DNN_BACKEND_OPENCV);				//setPreferableBackend实际计算后台,default默认是DNN_BACKEND_OPENCV作为计算后台,使用此就行(也有加速后台ENGINE)
	net.setPreferableTarget(DNN_TARGET_CPU);						//设置在什么设备上进行计算(opencl(需要有interl图形卡)、FPGA、CPU)
	//当上面两个设置之后,他在执行网络进行推算时就会执行此计算后台进行计算(不同的计算后台有不同的效果,速度也有差别)

	//获取各层信息
	vector<string> layer_names = net.getLayerNames();		//此时我们就可以获取所有层的名称了,有了这些可以将其ID取出
	for (int i = 0; i < layer_names.size(); i++) {
		int id = net.getLayerId(layer_names[i]);			//通过name获取其id
		auto layer = net.getLayer(id);						//通过id获取layer
		printf("layer id:%d,type:%s,name:%s\n", id, layer->type.c_str(), layer->name.c_str());	//将每一层的id,类型,姓名打印出来(可以明白此网络有哪些结构信息了)
		//printf("name2:%s\n", layer_names[i].c_str());
	}
	
	Mat src = imread("G:/OpenCV/opencv笔记所用图片/plane.jpg");
	imshow("src", src);

	因为googlenet的通道类型是RGB通道的,而OpenCVread读取的是BGR通道的,要进行通道转换(实际在后面的blobFromImage就能进行通道转换,此处无需转换)
	//Mat rgb;	
	//cvtColor(src, rgb, COLOR_BGR2RGB);

	//构建输入
	//使用blobFromImage函数对单张图像进行预处理,使其符合网络的输入
	//对于网络来说原始输入层的时候都有指定的输入大小
	int w = 224;
	int h = 224;
Mat inputBlob = blobFromImage(src, 1.0, Size(w, h),Scalar(104, 117, 123), false,false);	//我们要将图像resize成224*224的才是我们神经网络可以接受的宽高
	//参数1:输入图像,参数2:默认1.0表示0-255范围的,参数3:设置输出的大小,参数4:均值对所有数据中心化预处理,参数5:是否进行通道转换,参数6:,参数7:默认深度为浮点型

	//设置输入
	//前面已经将图像预处理完成,就可以执行分类了
	//现在要将其输入到创建的网络中
	net.setInput(inputBlob);

	//进行推断得到输出
	//让网络执行得到output,调用forward可以得到一个结果
	//此处不给参数,得到的是最后一层的结果,也可以输入层数得到任何一层的输出结果
	Mat probMat = net.forward();	//通过前面的输出层看最后一层,可以知道输出100个分类,每个分类的得分是多少
									//softmax层出来的总和为1,找到max值对应的index就知道对应的分类了,在classification_classes_ILSVRC2012.txt可以知道具体名称
	//上方得到的probMat是1000*1*1(有imagewatch得到)

	//要解析输出(forward得到的)
	//对数据进行序列化(变成1行n列的,可以在后面进行方便的知道是哪个index了)
	Mat prob = probMat.reshape(1, 1);		//reshape函数可以进行序列化,(输出为1通道1行的数据,参数1:1个通道,参数2:1行)将输出结果变成1行n列的,但前面probMat本身就是1000*1*1
											//实际结果probMat和prob相同
											//当其他网络probMat需要序列化的时候,reshape就可以了
	//此时找到最大的那个
	Point classNum;
	double classProb;
	minMaxLoc(prob, NULL, &classProb, NULL, &classNum);//此时只获取最大值及最大值位置,最小值不管他
	int index = classNum.x;		//此时得到的是最大值的列坐标。就是其类的索引值,就可以知道其类名了
	printf("\n current index=%d,possible:%2f\n",index,classProb);	// current index=812,possible:0.999675,用editplus打开classification_classes_ILSVRC2012.txt,找到第813行(因为812+1)就是结果
	//这样自己在classification_classes_ILSVRC2012.txt找答案挺麻烦的,我们使用程序打开此文件,得到类名即可


	waitKey(0);
	return 0;
}

5-读取分类标签文件显示分类

一般对于自然场景的图像可以进行较精确的分类,利于后期图像查找等

输出层解析:主要讲其变为我们能接收的对象,找出最大可能性及其对于index

标签匹配:读入txt文件使其与index一一对应,最终得到类名

#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>			//包含dnn模块的头文件
#include <iostream>
#include <fstream>				//文件流进行txt文件读取

using namespace cv;
using namespace cv::dnn;			//包含dnn的命名空间
using namespace std;

vector<string> readLabels();		//将类名的txt文件读取到容器中

int main() {

	string bin_model = "D:/OpenCV/project/opencv_tutorial-master/data/models/googlenet/bvlc_googlenet.caffemodel";		//定义模型权重文件的加载路径
	string protxt = "D:/OpenCV/project/opencv_tutorial-master/data/models/googlenet/bvlc_googlenet.prototxt";			//定义模型描述文件的加载路径
	//Imagenet支持1000个分类,分类类别在classification_classes_ILSVRC2012.txt可以看到,一直到1000

	vector<string> names = readLabels();		//使用自定义函数读取特定文件中的类名到names中,在后面输出对应名称时会用到

	//有了模型的权重和描述文件就可以加载模型了
	//在OpenCV中加载模型要通过readNetWork,
	//readNet函数有三个参数,参数1加载网络(caffe、TensorFlow等),参数2,权重路径,参数3,描述文件
	//或者readNetFromCaffe(TensorFlow、darknet、ONNX等),意思为读进来的必须是caffe模型,此外还支持TensorFlow等模型,只有两个参数
	//还有readNetFromModelOptimizer从dnn模型优化器中读取模型优化后的模型
	Net net = readNetFromCaffe(protxt, bin_model);

	//设置计算后台(OpenCVdnn模块支持设置不同的计算后台和在不同的设备上进行)
	net.setPreferableBackend(DNN_BACKEND_OPENCV);				//setPreferableBackend实际计算后台,default默认是DNN_BACKEND_OPENCV作为计算后台,使用此就行(也有加速后台ENGINE)
	net.setPreferableTarget(DNN_TARGET_CPU);						//设置在什么设备上进行计算(opencl(需要有interl图形卡)、FPGA、CPU)
	//当上面两个设置之后,他在执行网络进行推算时就会执行此计算后台进行计算(不同的计算后台有不同的效果,速度也有差别)

	//获取各层信息
	vector<string> layer_names = net.getLayerNames();		//此时我们就可以获取所有层的名称了,有了这些可以将其ID取出
	for (int i = 0; i < layer_names.size(); i++) {
		int id = net.getLayerId(layer_names[i]);			//通过name获取其id
		auto layer = net.getLayer(id);						//通过id获取layer
		printf("layer id:%d,type:%s,name:%s\n", id, layer->type.c_str(), layer->name.c_str());	//将每一层的id,类型,姓名打印出来(可以明白此网络有哪些结构信息了)
		//printf("name2:%s\n", layer_names[i].c_str());
	}
	
	Mat src = imread("G:/OpenCV/opencv笔记所用图片/plane.jpg");
	imshow("src", src);

	因为googlenet的通道类型是RGB通道的,而OpenCVread读取的是BGR通道的,要进行通道转换(实际在后面的blobFromImage就能进行通道转换,此处无需转换)
	//Mat rgb;	
	//cvtColor(src, rgb, COLOR_BGR2RGB);

	//构建输入
	//使用blobFromImage函数对单张图像进行预处理,使其符合网络的输入
	//对于网络来说原始输入层的时候都有指定的输入大小
	int w = 224;
	int h = 224;
	Mat inputBlob = blobFromImage(src, 1.0, Size(w, h),Scalar(104, 117, 123), false,false);	//我们要将图像resize成224*224的才是我们神经网络可以接受的宽高
	//参数1:输入图像,参数2:默认1.0表示0-255范围的,参数3:设置输出的大小,参数4:均值对所有数据中心化预处理,参数5:是否进行通道转换,参数6:,参数7:默认深度为浮点型


	//设置输入
	//前面已经将图像预处理完成,就可以执行分类了
	//现在要将其输入到创建的网络中
	net.setInput(inputBlob);

	//进行推断得到输出
	//让网络执行得到output,调用forward可以得到一个结果
	//此处不给参数,得到的是最后一层的结果,也可以输入层数得到任何一层的输出结果
	Mat probMat = net.forward();	//通过前面的输出层看最后一层,可以知道输出100个分类,每个分类的得分是多少
									//softmax层出来的总和为1,找到max值对应的index就知道对应的分类了,在classification_classes_ILSVRC2012.txt可以知道具体名称
	//上方得到的probMat是1000*1*1(有imagewatch得到)

	//要解析输出(forward得到的)
	//对数据进行序列化(变成1行n列的,可以在后面进行方便的知道是哪个index了)
	Mat prob = probMat.reshape(1, 1);		//reshape函数可以进行序列化,(输出为1通道1行的数据,参数1:1个通道,参数2:1行)将输出结果变成1行n列的,但前面probMat本身就是1000*1*1
											//实际结果probMat和prob相同
											//当其他网络probMat需要序列化的时候,reshape就可以了
	//此时找到最大的那个
	Point classNum;
	double classProb;
	minMaxLoc(prob, NULL, &classProb, NULL, &classNum);//此时只获取最大值及最大值位置,最小值不管他
	int index = classNum.x;		//此时得到的是最大值的列坐标。就是其类的索引值,就可以知道其类名了
	printf("\n current index=%d,possible:%2f,name=%s\n",index,classProb,names[index].c_str());	// current index=812,possible:0.999675,用editplus打开classification_classes_ILSVRC2012.txt,找到第813行(因为812+1)就是结果
	//这样自己在classification_classes_ILSVRC2012.txt找答案挺麻烦的,我们使用程序打开此文件,得到类名即可

	//此时可以将名称打印到图片上去
	putText(src, names[index].c_str(), Point(50, 50), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 255), 2, 8);
	imshow("result", src);

	waitKey(0);
	return 0;
}


vector<string> readLabels() {
	string label_map_txt = "D:/OpenCV/project/opencv_tutorial-master/data/models/googlenet/classification_classes_ILSVRC2012.txt";
	vector<string> classNames;
	ifstream fp(label_map_txt);
	if (!fp.is_open()) {
		printf("could not find the file \n");
		exit(-1);
	}
	std::string name;
	while (!fp.eof()) {		//当没有结束时就一直读取
		getline(fp, name);	//每次读取一行
		if (name.length()) {
			classNames.push_back(name);
		}
	}
	fp.close();		//关闭文件输入流
	return classNames;
}

6-对象检测网络介绍

前面学习了如何通过DNN实现图像的分类,

图像分类的预训练模型googlenet,哪些层,输入,输出哪些看到

怎么去set到网络中,forward得到输出,可以forward任意一层,默认最后一层

对网络进行加载,相关后台设备,网络分类。

OpenCV的DNN模块只能进行推理,不能执行训练,训练是有各个深度学习的框架完成的,

这节课学习对象检测模型。

在计算机视觉中对象检测,常见模型及其输出层格式不同。

对象检测模型进行应用的代码演示。

前面的是分类,现在要检测:

图像的分类加对象位置信息就是对象检测

前面的googlenet只是得到分类信息就可以了

而检测还需要得到位置信息,长宽高等位置,得到位置信息再进行矩形

Image classification 图像分类

Object detection 对象检测

这些模型都有一些结构性的不同

卷积提取特征,RPN区域推荐网络,输出会产生很多ROI区域,可以对ROI区域分类,有了分类信息和位置信息

RCNN分类,位置回归,对象在什么位置,可能性是多少

SSD缺点,如果目标太小就检测不到了,对小物体检测效果不太好。

非最大抑制,可能会在不同的层次匹配到对象

SSD全是卷积的神经网络,训练,速度快,很多时候使用

YOLO也慢慢变成全卷积的网络了

Scale都是有阈值的,大于0.5的保留,小于的去掉

YOLOV3多尺度输出

YOLOV3输出的是其中心位置和宽高,解析方式与faster RCNN不同

YOLO最后一层的输出方式

11 N7 N是多少张图,7个值,1,bumbox,index,scale,4个位置信息
1
1N(5+score) 矩形信息和得分

不同网络最后一层输出方式不同,代码使用也不同

7-SSD对象检测网络加载与执行

SSD一次就能检测出多个box

SSD基础网络不一样,导出权重大小不一样的离线模型文件

数据集都是室内常见的物体,书,车,人,狗等类别

得分越高,在此数据集的表现越好

Scale是2/255

上面是模型的相关介绍

推断预测,结果解析,

data就是参数层的名称

CV_WRAP void setInput(InputArray blob, const String& name = "",
                              double scalefactor = 1.0, const Scalar& mean = Scalar());

        /** @brief Sets the new value for the learned param of the layer.
         *  @param layer name or id of the layer.
         *  @param numParam index of the layer parameter in the Layer::blobs array.
         *  @param blob the new value.
         *  @see Layer::blobs
         *  @note If shape of the new blob differs from the previous shape,
         *  then the following forward pass may fail.
        */

Forward 最终输出层就是detection_out

detection_out这一层出来的就是117的

D:\OpenCV\opencv-4.4.0-vc14_vc15\opencv\sources\samples\dnn在其下方的moduls.yml文件中有关于各个模型的参数配置,

而其labelmap_det.txt文件中内容有点多,实际可以总结出

如下

String objNames[] = { "background",
"aeroplane","bicycle","bird","boat",
"bottle","bus","car","cat","chair",
"cow","diningtable","dog","horse",
"motorbike","person","pottedplant",
"sheep","sofa","train","tvmonitor" };

输入层就是data

获取的层信息发现,最后一层就是detection_out


8-SSD对象检测推理输出解释与显示

加载输入,解析输出,后面会有针对视频的对象检测

#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>			//包含dnn模块的头文件
#include <iostream>
#include <fstream>				//文件流进行txt文件读取

using namespace cv;
using namespace cv::dnn;			//包含dnn的命名空间
using namespace std;

String objNames[] = { "background",
"aeroplane","bicycle","bird","boat",
"bottle","bus","car","cat","chair",
"cow","diningtable","dog","horse",
"motorbike","person","pottedplant",
"sheep","sofa","train","tvmonitor" };

int main() {

	string bin_model = "D:/OpenCV/project/opencv_tutorial-master/data/models/ssd/MobileNetSSD_deploy.caffemodel";		//定义模型权重文件的加载路径
	string protxt = "D:/OpenCV/project/opencv_tutorial-master/data/models/ssd/MobileNetSSD_deploy.prototxt";			//定义模型描述文件的加载路径

	//load DNN model
	Net net = readNetFromCaffe(protxt, bin_model);

	//设置计算后台(OpenCVdnn模块支持设置不同的计算后台和在不同的设备上进行)
	net.setPreferableBackend(DNN_BACKEND_OPENCV);				//setPreferableBackend实际计算后台,default默认是DNN_BACKEND_OPENCV作为计算后台,使用此就行(也有加速后台ENGINE)
	net.setPreferableTarget(DNN_TARGET_CPU);						//设置在什么设备上进行计算(opencl(需要有interl图形卡)、FPGA、CPU)

	//获取各层信息
	vector<string> layer_names = net.getLayerNames();		//此时我们就可以获取所有层的名称了,有了这些可以将其ID取出
	for (int i = 0; i < layer_names.size(); i++) {
		int id = net.getLayerId(layer_names[i]);			//通过name获取其id
		auto layer = net.getLayer(id);						//通过id获取layer
		printf("layer id:%d,type:%s,name:%s\n", id, layer->type.c_str(), layer->name.c_str());	//将每一层的id,类型,姓名打印出来(可以明白此网络有哪些结构信息了)
	}

	Mat src = imread("G:/OpenCV/opencv笔记所用图片/gou.jpg");	//plane
	if (src.empty()) {
		cout << "could not load image.." << endl;
		getchar();
		return -1;
	}
	imshow("src", src);

	//构建输入(根据models.yml)
	Mat blob = blobFromImage(src, 0.007843, Size(300, 300), Scalar(127.5, 127.5, 127.5), false, false);	//交换通道false,是否剪切false
	net.setInput(blob, "data");		//输入层名称就是data,通过描述文件也可以看到输入层名称

	//推测结果
	Mat detection = net.forward("detection_out");	//将名称为detection_out的层的结果返回,不输入名称默认也是返回最后一层,输入可以返回特定层的结果
	//上方得到的detection宽高为0(使用imagewatch查看,原因是其为tensen,它的宽高维度等信息都存在size结构中,所以要在下方通过size获取)
													
	//重新定义一个Mat对象接收
	Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());		//此处是初始化一个行为size[2],列为size[3]大小,深度为浮点型,数据从detection中获取
																								//使用imagewatch可以看到7*100的数组,单通道32F深度
	
	//获取浮点型数据后就可以进行数据的解析了
	float confidence_threshold = 0.5;		//在后面通过此判断合适的结果进行保存
	

	//解析输出数据
	//上方获取的detectionMat是100行,7列的,每行对应一个对象
	//输出结果[1x1xNx7], 其中输出的七个维度浮点数如下:
	//[image_id, label, conf, x_min, y_min, x_max, y_max]
	for (int i = 0; i < detectionMat.rows; i++) {
		float score = detectionMat.at<float>(i, 2);			//此处获取的是conf(i, 2),也就是得分
		if (score > confidence_threshold) {					//如果获取的score大于0.5,就将box保存,不要扔掉
			size_t objIndex = (size_t)(detectionMat.at<float>(i, 1));		//此处获取的是label(i, 2),就是索引
			//得到四个点的矩形框位置
			float tl_x = detectionMat.at<float>(i, 3)*src.cols;		//x_min(此处detectionMat.at<float>(i, 3)得到的是一个图像的比例值不是真实值,需要变换)
			float tl_y = detectionMat.at<float>(i, 4)*src.rows;		//y_min
			float br_x = detectionMat.at<float>(i, 5)*src.cols;		//x_max
			float br_y = detectionMat.at<float>(i, 6)*src.rows;		//y_max
			//得到box
			Rect box((int)tl_x, (int)tl_y, (int)br_x - tl_x, (int)br_y - tl_y);
			//画矩形
			rectangle(src, box, Scalar(0, 0, 255), 2, 8, 0);
			//写文字(在box的做上角写文字)
			putText(src, format("score:%2f,%s",score,objNames[objIndex].c_str()), box.tl(), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(255, 0, 0), 2, 8);
		}
	}
	imshow("ssd-detection-demo", src);

	waitKey(0);
	return 0;
}

9-Faster RCNN对象检测模型使用

前面是SSD对象检测网络实现离线运行进行检测

加载模型,推理,输出,解析,输出格式是11 N*7

至于网络中间部分训练时会关心,而推测评估时,只需知道输入输出,解析输出结果即可,

也可以获取卷积各层信息

Faster-RCNN是TensorFlow对象检测,TensorFlow object API

怎么将TensorFlow支持的对象拿出来


一定不要将其拷贝代码

将下载的描述文件复制到此文件目录下

上面的很多网络都是来自TensorFlow的预训练框架,已经训练好了,只需要将其权重及描述文件下载下来即可离线使用

点击进入,点击row另存为,不要拷贝会破坏格式。

本处下载的是faster-RCNN resnet50的权重文件及配置文件

下载的配置文件,后缀会有.txt, 重命名将多余的.txt删除

解决无法打开RAW的问题

Windows上可以这样办:

https://www.ipaddress.com查一下raw.githubusercontent.com的ipv4地址,比如我现在查到的是199.232.68.133。

使用管理员权限打开C:\Windows\System32\drivers\etc\hosts文件,添加到最后一行

199.232.68.133 raw.githubusercontent.com 

或者管理员权限开或者给个用户写入权限。然后差不多就行了,可能要重启。不过本人使用editplus将其打开并输入后再打开raw.githubusercontent.com就成功了。

D:\OpenCV\project\opencv_tutorial-master\data\models\mscoco_label_map.pbtxt

数据集的类名有80个,在上面的文件内

80个类别定义数组太麻烦,直接采用读取读取.pbtxt文件的方式将类名导入进来

上面的格式

D:\OpenCV\project\opencv_tutorial-master\data\models\TensorFlow\faster_rcnn_resnet50_coco_2018_01_28

frozen_inference_graph.pb

faster_rcnn_resnet50_coco_2018_01_28.pbtxt

有时检测对象不多,将confidence值调低一些如0.5变为0.3如下所示可以检测出(但可能有些出错,一般以0.5即可)

此网络模型要比SSD耗时多

10-YOLOv3对象检测模型推理使用

Faster-RCNN来自TensorFlow的object detection即对象检测框架,此框架也有其他的对象检测网络,可以也被OpenCV4的DNN模块接收,并且数据设置与faster-RCNN类似

SSD来自caffe

Faster-RCNN来自TensorFlow

YOLO来自darknet框架,是C语言的框架,主要为YOLO服务

COCO数据集支持80个分类

常用的物体基本在其内

配置文件与权重

https://pjreddie.com/darknet/yolo/

YOLOV3-416 表示宽高416

YOLO有多个输出层,不像faster-RCNN、SSD只有一个输出层

YOLO有多个输出层,在这个层发现,另一个层也发现了,需要调用NMS将所有输出层结果合并

输出

NMS去掉重复box

Yolo有几个输出层,可能在好几个层都检测到了然后输出

NMS


#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>			//包含dnn模块的头文件
#include <iostream>
#include <fstream>				//文件流进行txt文件读取

using namespace cv;
using namespace cv::dnn;			//包含dnn的命名空间
using namespace std;

string label_map = "D:/OpenCV/project/opencv_tutorial-master/data/models/YOLO3/object_detection_classes_yolov3.txt";

int main() {

	string weight_file = "D:/OpenCV/project/opencv_tutorial-master/data/models/YOLO3/yolov3.weights";		//定义模型权重文件的加载路径
	string cfg_file = "D:/OpenCV/project/opencv_tutorial-master/data/models/YOLO3/yolov3.cfg";			//定义模型描述文件的加载路径
	

	//load DNN model
	Net net = readNetFromDarknet(cfg_file, weight_file);
	vector<String> outNames = net.getUnconnectedOutLayersNames();	//获取多个输出层
	for (int k = 0; k < outNames.size(); k++) {
		printf("output layer name:%s \n", outNames[k].c_str());
	}

	//设置计算后台(OpenCVdnn模块支持设置不同的计算后台和在不同的设备上进行)
	net.setPreferableBackend(DNN_BACKEND_OPENCV);				//setPreferableBackend实际计算后台,default默认是DNN_BACKEND_OPENCV作为计算后台,使用此就行(也有加速后台ENGINE)
	net.setPreferableTarget(DNN_TARGET_CPU);						//设置在什么设备上进行计算(opencl(需要有interl图形卡)、FPGA、CPU)

	//获取各层信息
	vector<string> layer_names = net.getLayerNames();		//此时我们就可以获取所有层的名称了,有了这些可以将其ID取出
	for (int i = 0; i < layer_names.size(); i++) {
		int id = net.getLayerId(layer_names[i]);			//通过name获取其id
		auto layer = net.getLayer(id);						//通过id获取layer
		printf("layer id:%d,type:%s,name:%s\n", id, layer->type.c_str(), layer->name.c_str());	//将每一层的id,类型,姓名打印出来(可以明白此网络有哪些结构信息了)
	}

	Mat src = imread("G:/OpenCV/opencv笔记所用图片/gou.jpg");	//plane
	if (src.empty()) {
		cout << "could not load image.." << endl;
		getchar();
		return -1;
	}
	imshow("src", src);

	Mat blob = blobFromImage(src, 0.00392, Size(416, 416), Scalar(), true, false);
	net.setInput(blob);

	//推理输出
	vector<Mat> outs;
	net.forward(outs, layer_names);	//将三个输出层的结果都得到
	vector<Rect> boxes;		//三个输出层的box信息接收
	vector<int> classIds;	//接收三个类的信息
	vector<float> confidences;	//接收三个得分信息

	for (int i = 0; i < outs.size(); i++) {
		//开始解析每个输出blob
		float *data = (float*)outs[i].data;
		for (int j = 0; j < outs[i].rows; j++,data+=outs[i].cols) {			//加入data+=outs[i].cols是为了跳到下一行去,data相当于指针,指针到下一行使用此种方式
			Mat scores = outs[i].row(j).colRange(5, outs[i].cols);
			Point classIdPoint;
			double confidence;
			minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
			//[C, center_x, center_y, width, height]
			if (confidence > 0.5) {
				int centerx = (int)(data[0] * src.cols);
				int centery = (int)(data[1] * src.rows);
				int width = (int)(data[2] * src.cols);
				int height = (int)(data[3] * src.rows);
				int left = centerx - width / 2;
				int top = centery - height / 2;
				classIds.push_back(classIdPoint.x);
				boxes.push_back(Rect(left, top, width, height));
				rectangle(src, Rect(left, top, width, height), Scalar(0, 0, 255), 2, 8);
			}
		}
	}
	imshow("result", src);

	waitKey(0);
	return 0;
}

发现有多个框,这是没有做非最大抑制出现的。

OpenCVDNN模块已经提供了非最大抑制的API

Yolov3tiny 也可以尝试一下,速度较快,只有30M左右,精度较低

但对于实时性较高的场景,还是不错的

NMS可以去掉重复的检测框,但需要调试,根据结果选择调试

11-OpenCV DNN配置加速组件IE支持

代码演示部分,每层都有box框

对象检测,图像分类都用了,但他们的速度太慢

使用IE加速我们对模块的推理

英特尔发布的深度学习加速工具包,开源的

其中最重要的组件有IE

• OpenVINO中的IE加速组件介绍

• 设置IE为计算后台,加速执行

• 代码演示

注意在官方下载的是无加速的,需要自己编译,才能得到支持OpenCV版本的,此种方式较麻烦

我们无需编译,只需要安装openVINO就可以得到一个支持IE加速的版本

OpenVINO中的IE加速组件介绍

• 下载与安装IE支持的OpenCV版本

• 重新配置OpenCV

• 安装依赖(VS2015/VS2017+CMake3.14+Python3.6.5)

https://software.intel.com/en-us/openvino-toolkit/choose-download

注意要有CMAKE3.14,的环境变量要配置,python3.6.5也要配置到环境变量中去

注意openvino只会从默认的安装目录去找,所以前面的不要自定义安装,默认安装即可

OpenCVvino路径是可以改的,可以改到

将OpenCVvino中的OpenCV的lib,include等配置好

C:\Program Files (x86)\IntelSWTools\openvino_2020.4.287

只有通过OpenCVvino安装好后,才可以设置计算后台如下,否则会报错

net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE);

net.setPreferableTarget(DNN_TARGET_CPU);

可能对图片时,发现无法明显加速,前期是在加载模型,初始化权重方法,但一旦推断forward速度就会非常快

前面的caffe、TensorFlow、mxnet等平台的离线模型都可以通过OpenCV的DNN模块加载,加载开始计算时使用OpenCVDNN作为加载后台,现在有了opencvvino,就可以使用inference ENgine作为后台支持,而此后台又支持CPU\GPU\VPU加速,英伟达的GPU天生不支持,支持的是英特尔的图形卡的计算方式,如果想要支持需要自己想办法编译。

获得优化后可以得到5到10倍的加速,一秒钟可能5帧,现在就25帧了

代码实现

• 设置IE为计算后台

• 加速执行

• 支持网络与模型

• 关于转换IR与更多知识...

安装完成后需要测验一下

加速组件

https://cmake.org/files/v3.14/

https://www.python.org/downloads/release/python-365/




注意cmake和python3.6.5要默认路径安装,

Openvino可以更改路径安装

下面的路径就是推算引擎

C:\Program Files (x86)\IntelSWTools\openvino_2020.4.287\inference_engine

我们的是debug模式,要将下方的路径添加到环境变量

C:\Program Files (x86)\IntelSWTools\openvino_2020.4.287\inference_engine\bin\intel64\Debug

此处有OpenCV

C:\Program Files (x86)\IntelSWTools\openvino_2020.4.287\opencv

之前如何配置这里就怎么配置

OpenCV配置好后,此处就可以使用了

如何检测是否安装好了

C:\Program Files (x86)\IntelSWTools\openvino_2020.4.287\deployment_tools\demo

进入文件下,双击运行demo_security_barrier_camera.bat

等到最后弹出车牌检测图,说明所有安装都没有问题

使用openvino的OpenCV的属性配置,看能否

net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE);运行。

opencv_calib3d440d.lib
opencv_core440d.lib
opencv_dnn440d.lib
opencv_features2d440d.lib
opencv_flann440d.lib
opencv_gapi440d.lib
opencv_highgui440d.lib
opencv_imgcodecs440d.lib
opencv_imgproc440d.lib
opencv_ml440d.lib
opencv_objdetect440d.lib
opencv_photo440d.lib
opencv_stitching440d.lib
opencv_video440d.lib
opencv_videoio440d.lib



inference_engine_c_apid.lib
inference_engine_ir_readerd.lib
inference_engine_legacyd.lib
inference_engine_lp_transformationsd.lib
inference_engine_onnx_readerd.lib
inference_engine_preprocd.lib
inference_engine_transformationsd.lib
inference_engined.lib
相关推荐
nfgo5 分钟前
Apollo自动驾驶项目(二:cyber框架分析)
人工智能·自动驾驶·unix
h1771134720511 分钟前
基于区块链的相亲交易系统源码解析
大数据·人工智能·安全·系统架构·交友
HPC_fac1305206781621 分钟前
RTX 4090 系列即将停产,RTX 5090 系列蓄势待发
服务器·人工智能·gpu算力
xuehaisj1 小时前
论文内容分类与检测系统源码分享
人工智能·分类·数据挖掘
大耳朵爱学习1 小时前
大模型预训练的降本增效之路——从信息密度出发
人工智能·深度学习·机器学习·自然语言处理·大模型·llm·大语言模型
loongloongz2 小时前
联合条件概率 以及在语言模型中的应用
人工智能·语言模型·自然语言处理·概率论
lijfrank2 小时前
情感计算领域期刊与会议
人工智能·人机交互
sp_fyf_20242 小时前
计算机人工智能前沿进展-大语言模型方向-2024-09-18
人工智能·语言模型·自然语言处理
sp_fyf_20242 小时前
计算机人工智能前沿进展-大语言模型方向-2024-09-14
人工智能·语言模型·自然语言处理
ybdesire2 小时前
nanoGPT用红楼梦数据从头训练babyGPT-12.32M实现任意问答
人工智能·深度学习·语言模型