一、深度神经网络模型
-
openCV DNN支持的功能:图像分类、对象检测、图像分割、场景文字检测、人脸检测与识别
-
openCV DNN支持的对象检测网络:Faster-RCNN、SSD(VGG/mobile-net backbone)、YOLO(YOLOv3/YOLOv3-tiny/YOLOv4)
输入------------------>黑盒------------------------>输出
二、OpenCV使用训练好的网络模型
- OpenCV dnn module只能执行推理(对训练好的模型加载使用),不能执行网络的训练过程。
- 当下OpenCV支持的深度学习框架和相关层:
Deep Learning in OpenCV · opencv/opencv Wiki · GitHub - 深度神经网络模型Deep Neural Network module:OpenCV: Deep Neural Network module
三、SSD(Single Shot MultiBox Detector)
Opencv支持很多的深度学习框架,且有很多开源深度网络模型,以SSD模型为例:
MobileNetSSD_deploy.caffemodel
openCV支持的模型描述文件汇总:Site Unreachable
# Caffe implementation of SSD model from https://github.com/chuanqi305/MobileNet-SSD //模型来源地址
ssd_caffe:
load_info:
url: "https://drive.google.com/uc?export=download&id=0B3gersZ2cHIxRm5PMWRoTkdHdHc"
sha1: "994d30a8afaa9e754d17d2373b2d62a7dfbaaf7a"
model: "MobileNetSSD_deploy.caffemodel" //模型二进制文件名
config: "MobileNetSSD_deploy.prototxt" //模型描述文件名 ,二者均在模型来源地址下载
mean: [127.5, 127.5, 127.5]
scale: 0.007843
width: 300
height: 300
rgb: false
classes: "object_detection_classes_pascal_voc.txt"
sample: "object_detection"
模型描述文件
name: "MobileNet-SSD"
input: "data" //输入层名称
input_shape { //输入参数格式
dim: 1 //每次接受一张图片
dim: 3 //三通道
dim: 300
dim: 300 //图像尺寸300*300
}
.....//中间省略网络各层描述
layer {
name: "detection_out"
type: "DetectionOutput"
bottom: "mbox_loc"
bottom: "mbox_conf_flatten"
bottom: "mbox_priorbox"
top: "detection_out" //最后一层描述示例
include {
phase: TEST
}
detection_output_param {
num_classes: 21
share_location: true
background_label_id: 0
nms_param {
nms_threshold: 0.45
top_k: 100
}
- 输入: [ 1 × 3 × 300 × 300 ] [ 1\times3\times300\times300] [1×3×300×300]
- 输出: [ 1 × 1 × 100 × 7 ] [ 1\times1\times100\times7] [1×1×100×7] ,7对应的浮点数据如下:[image_id, label, conf, x_min, y_min, x_max, y_max]
- 示例:
四、OpenCV网络使用流程
参考资料:OpenCv-C+±深度神经网络(DNN)模块-使用goolenet模型实现图像分类
1. 加载网络模型
openCV支持的模型描述文件:Site Unreachable
Net cv::dnn::readNet(const String& model, //模型的二进制文件
const String& config="", //模型的配置参数,通常在模型的voc文件夹下寻找
const String& framework="" //具体的框架名
)
2. 计算后端
OpenCV dnn module支持设置不同的计算后端,在指定目标设备上进行。
void cv::dnn::Net::setPreferableBackend(int backendId);
void cv::dnn::Net::setPreferableTarget(int targetId);
3. 构建输入
Mat cv::dnn::blobFromImage(InputArray image,
double scalefactor = 1.0,
const Size & size = Size(),
const Scalar & mean = Scalar(),
bool swapRB = false,
bool crop = false,
int ddepth = CV_32F
)
4. 执行推理inference
Mat cv::dnn::Net::forward(const String& outputName=String())
5.解析输出
使用 detection函数
五、完整代码
参考资料:深度神经网络(DNN)之二(使用SSD进行对象检测)
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp> //包含dnn模块的头文件
#include <iostream>
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查看,原因是其为tensor,它的宽高维度等信息都存在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;
}
六、TensorFlow Object Detection API
上述介绍的是Caff框架下的SSD模型,同理亦可以使用TensorFlow框架下的模型如YOLO