OpenCV4深度神经网络DNN实战教程
- 1-概述与环境配置
- 2-卷积神经网络概述
- 3-加载网络模型与设置
-
- [3.1 所需的模型下载](#3.1 所需的模型下载)
- [3.2 所使用的模型(googlenet)](#3.2 所使用的模型(googlenet))
- [3.3 Net介绍](#3.3 Net介绍)
- [3.4 代码输出网络结构信息](#3.4 代码输出网络结构信息)
- [3.5 计算后台设置](#3.5 计算后台设置)
- [3.6 所有代码](#3.6 所有代码)
- 4-图像分类网络inception的使用
- 5-读取分类标签文件显示分类
- 6-对象检测网络介绍
- 7-SSD对象检测网络加载与执行
- 8-SSD对象检测推理输出解释与显示
- [9-Faster RCNN对象检测模型使用](#9-Faster RCNN对象检测模型使用)
- 10-YOLOv3对象检测模型推理使用
- [11-OpenCV DNN配置加速组件IE支持](#11-OpenCV DNN配置加速组件IE支持)
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