一、OpenCV DNN 简介
OpenCV中的深度学习模块(DNN)只提供了推理功能,不涉及模型的训练,支持多种深度学习框架,比如TensorFlow,Caffe,Torch和Darknet。
- opencv 无法训练模型,但它支持载入其他深度学习框架训练好的模型,并使用该模型进行预测 inference;
- opencv 在载入模型时会使用 dnn 模块对模型进行重写,使得模型运行效率更高。
**轻量型:**DNN模块只实现了推理功能,代码量及编译运行开销远小于其他[深度学习模型框架。
**使用方便:**DNN模块提供了内建的CPU和GPU加速,无需依赖第三方库,若项目中之前使用了OpenCV,那么通过DNN模块可以很方便的为原项目添加深度学习的能力。
通用性: DNN模块支持多种网络模型格式,用户无需额外的进行网络模型的转换就可以直接使用,支持的网络结构涵盖了常用的目标分类,目标检测和图像分割的类别。
支持多种运算设备: DNN模块支持多种运算设备(CPU,GPU等)和操作系统(Linux,windows,MacOS等)。
二、YOLO简介
YOLO (You Only Look Once) 是一种实时目标检测模型,它采用卷积神经网络CNN直接回归边界框(bounding box)的位置及其所属类别。
官方地址为:
https://pjreddie.com/darknet/yolo/
Github地址为:
https://github.com/pjreddie/darknet/tree/master
三、OpenCV加载YOLO模型实现目标检测
我们通过 OpenCV的DNN模块加载YOLO模型,并进行物体识别。
1、加载Yolo模型
在C++中调用Yolo模型之前,我们需要加载模型并进行初始化。以下是加载Yolo模型的基本步骤:
① 使用OpenCV读取图像
cv::Mat image = cv::imread("image.jpg");
② 定义Yolo模型配置文件和权重文件的路径
std::string configPath = "yolov3.cfg";
std::string weightPath = "yolov3.weights";
③ 加载Yolo模型配置文件和权重文件
cv::dnn::Net net = cv::dnn::readNetFromDarknet(configPath, weightPath);
加载完成后,我们就可以使用Yolo模型进行目标检测了。
2、Yolo数据集下载
YOLO模型需要配置文件、权重文件和识别问价,下面列出获取方式。
① 获取配置和权重文件
定义Yolo模型配置文件和权重文件分别为"yolov3.cfg"
和 "yolov3.weights"
,需要获取。
获取对应的.cfg文件和.weights文件:
地址:
点击下面的红框框即可下载。
备注: 我下载使用的是 《YOLOv3-416》。
② 获取yolov3.txt配置文件
当识别物体对应的名称时使用到 "object_detection_classes_yolov3.txt"
配置文件,下载地址如下:
https://github.com/arunponnusamy/object-detection-opencv/tree/master
object_detection_classes_yolov3.txt
内容:
person
bicycle
car
motorcycle
airplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
couch
potted plant
bed
dining table
toilet
tv
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush
③ 整理数据集
创建 models/yolov3/
目录,将 yolov3.cfg
、yolov3.weights
和 object_detection_classes_yolov3.txt
放到 models/yolov3/
目录下。
3、C++示例工程
① 源代码
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <opencv2/dnn.hpp>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
using namespace cv::dnn;
//数据集:cfg和weights加载位置
String yolo_cfg = "C:\\VS2019Output\\OpencvDemo1\\models\\yolov3\\yolov3.cfg";
String yolo_model = "C:\\VS2019Output\\OpencvDemo1\\models\\yolov3\\yolov3.weights";
int main(int argc, char** argv)
{
//加载网络模型
Net net = readNetFromDarknet(yolo_cfg, yolo_model);
//net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE);
net.setPreferableTarget(DNN_TARGET_CPU);
std::vector<String> outNames = net.getUnconnectedOutLayersNames();
for (int i = 0; i < outNames.size(); i++)
{
//输出各层信息
printf("output layer name : %s\n", outNames[i].c_str());
}
//从类名文件中读取类名
vector<string> classNamesVec;
ifstream classNamesFile("C:\\VS2019Output\\OpencvDemo1\\models\\yolov3\\object_detection_classes_yolov3.txt");
if (classNamesFile.is_open())
{
string className = "";
while (std::getline(classNamesFile, className))
classNamesVec.push_back(className);
}
// 加载图像
Mat frame = imread("volo3.png");
Mat inputBlob = blobFromImage(frame, 1 / 255.F, Size(416, 416), Scalar(), true, false);
net.setInput(inputBlob);
// 检测
std::vector<Mat> outs;
net.forward(outs, outNames);
vector<double> layersTimings;
double freq = getTickFrequency() / 1000;
double time = net.getPerfProfile(layersTimings) / freq;
ostringstream ss;
ss << "detection time: " << time << " ms";
putText(frame, ss.str(), Point(20, 20), 0, 0.5, Scalar(0, 0, 255));
vector<Rect> boxes;
vector<int> classIds;
vector<float> confidences;
for (size_t i = 0; i < outs.size(); ++i)
{
// Network produces output blob with a shape NxC where N is a number of
// detected objects and C is a number of classes + 4 where the first 4
// numbers are [center_x, center_y, width, height]
float* data = (float*)outs[i].data;
for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols)
{
Mat scores = outs[i].row(j).colRange(5, outs[i].cols);
Point classIdPoint;
double confidence;
minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
if (confidence > 0.5)
{
int centerX = (int)(data[0] * frame.cols);
int centerY = (int)(data[1] * frame.rows);
int width = (int)(data[2] * frame.cols);
int height = (int)(data[3] * frame.rows);
int left = centerX - width / 2;
int top = centerY - height / 2;
classIds.push_back(classIdPoint.x);
confidences.push_back((float)confidence);
boxes.push_back(Rect(left, top, width, height));
}
}
}
vector<int> indices;
NMSBoxes(boxes, confidences, 0.5, 0.2, indices);
for (size_t i = 0; i < indices.size(); ++i)
{
int idx = indices[i];
Rect box = boxes[idx];
String className = classNamesVec[classIds[idx]];
putText(frame, className.c_str(), box.tl(), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(255, 0, 0), 2, 8);
rectangle(frame, box, Scalar(0, 0, 255), 2, 8, 0);
}
imshow("YOLOv3-Detections", frame);
waitKey(0);
return 0;
}