30 案例:实施人脸检测
30 案例:实施人脸检测
opencv知识点:
- 创建和操作综合人工神经网络 -
dnn::Net
类- 读取以
TensorFlow
框架格式存储的网络模型 -readNetFromTensorflow
- 从图像创建4维
blob - blobFromImage
- 设置网络的新输入值-
Net::setInput
- 运行正向传递以计算指定层的输出 -
Net::forward
本课所解决的问题:
- 如何运行opencv4提供的人脸检测模型
1.OpenCV 4
本节课的内容主要基于opencv 4 实现,开始我们先了解一下opencv4
。
OpenCV4.x
发布以来,其依靠良好的接口代码、系统级别的优化、更加通用易学的函数调用,集成OpenVINO
与tensorflow
、caffe
等模型加速推断、实现了从传统的图像处理到基于深度学习的视觉处理路线图的完整拓展。
OpenCV4
毫无疑问是一个OpenCV
发展历史的一个重要里程碑之作。
官方的宣传口号OpenCV4 is more than OpenCV
也充分说明OpenCV4
是整合深度学习的新一代计算机视觉开发框架!
本课将介绍一种opencv4
自带的,一个进行人脸检测的模型
2.下载人脸检测模型和
为了方便,我们不采用视频的方式,我们就直接去下载课程源码zip了
下载完之后到图中目录:opencv_tutorial_data-master → models → face_detector
![7ed97a6c96de472989ecafc2d3cd3967](https://picture0717.oss-cn-beijing.aliyuncs.com/img/7ed97a6c96de472989ecafc2d3cd3967.png)
目标文件夹:安装opencv目录下的sources → samples → dnn → face_detector
![77232f0964cf46048e16f4f8bf63f0d6](https://picture0717.oss-cn-beijing.aliyuncs.com/img/77232f0964cf46048e16f4f8bf63f0d6.png)
3.人脸检测模型演示
开始前,我们可以先把头文件和命名空间补上
这里提一下,当我们使用Net导入模型时,关于模型的各种参数可以从这个文档查看
在运行这个人脸检测模型之前,我们需要先来了解几个API
dnn::Net类
readNetFromTensorflow
blobFromImage
Net::setInput
方法Net::forward
方法Mat::ptr
具体介绍如下
dnn::Net类
dnn::Net类
创建和操作综合人工神经网络
神经网络表示为有向无环图 (DAG),其中顶点是层实例,边指定层输入和输出之间的关系。
每个网络层在其网络内都有唯一的整数id 和唯一的字符串名称。LayerId 可以存储图层名称或图层ID。
此类支持其实例的引用计数,即副本指向同一个实例。
readNetFromTensorflow
readNetFromTensorflow
读取以TensorFlow框架格式存储的网络模型
共2个参数
第1个参数 pb文件路径
第2个参数 pbtxt文件路径
blobFromImage
blobFromImage
从图像创建4维blob(斑点,深度学习相关知识)
共7个参数
第1个参数 输入
第2个参数 图像空间大小(默认1.0,即图像仍保持在0~255色彩空间)
第3个参数 size
第4个参数 均值
第5个参数 是否交换RB通道(默认false)
第6个参数 调整图像大小后是否剪切(默认false)
第7个参数 输出的blob深度(默认CV_32F)
returns
按照NCHW(数量,通道,高度,宽度)顺序的4维Mat,blob就是这个4维Mat
Net::setInput方法
setInput
设置网络的新输入值
共4个参数
第1个参数 输入斑点
第2个参数 输入层名称(默认为空“”)
第3个参数 可选的标准化比列
第4个参数 可选的平均值
Net::forward方法
forward
运行正向传递以计算名为 outputName 的层的输出。
共1个参数
第1个参数 需要输出的层的名称
returns
返回也有四个值
第1个维度 表示有多少张图像,并且每个图像有一个编号。imageId
第2个维度 图像的批次。batchId
第3个维度 有多少个框(行)
第4个维度 每个框有7个值(列)
前两个值 标明类型 + index
第3个值 得分(得分越高,越有可能是人脸)
后四个值 矩形左上角和右下角的坐标
Mat::ptr
二维单通道元素可以用
MAT::at(i,j)
,i是行号,j是列号但对于多通道的非
uchar
类型矩阵来说,以上方法不适用
可以用Mat::ptr()
来获得指行某行元素的指针,在通过行数与通道数计算相应点的指针
演示如下:
//函数定义
void face_detection_demo();
//函数实现
void QuickDemo::face_detection_demo() {
//文件夹路径
string root_dir = "D:/meta/opencv/opencv-4.5.5/sources/samples/dnn/face_detector/";
//读取以TensorFlow框架格式存储的网络模型
dnn::Net net = dnn::readNetFromTensorflow(root_dir + "opencv_face_detector_uint8.pb", root_dir + "opencv_face_detector.pbtxt");
//对摄像头进行人脸检测
VideoCapture cap(0);
Mat frame;
while (true) {
cap.read(frame);//frame为输出,read是将捕获到的视频一帧一帧的传入frame
if (frame.empty()) {
break;
}
flip(frame, frame, 1);//左右翻转
//准备深度学习模型需要的数据 (blob-斑点)
Mat blob = dnn::blobFromImage(frame, 1.0, Size(300, 300), Scalar(104, 177, 123), false, false);
net.setInput(blob);
//完成推理
Mat probs = net.forward();
Mat detectionMat(probs.size[2], probs.size[3], CV_32F, probs.ptr<float>());
//解析结果
for (int i = 0; i < detectionMat.rows; i++) {
float confidence = detectionMat.at<float>(i, 2);//第三个值 得分
if (confidence > 0.5) {
//因为预测来的值为[0,1]范围的数,我们还需要*原图像的宽度和长度
int x1 = static_cast<int>(detectionMat.at<float>(i, 3) * frame.cols);
int y1 = static_cast<int>(detectionMat.at<float>(i, 4) * frame.rows);
int x2 = static_cast<int>(detectionMat.at<float>(i, 5) * frame.cols);
int y2 = static_cast<int>(detectionMat.at<float>(i, 6) * frame.rows);
Rect rect(x1, y1, x2 - x1, y2 - y1);
rectangle(frame, rect, Scalar(0, 0, 255), 2, 8, 0);
}
}
imshow("人脸检测",frame);
int c = waitKey(1);
if (c == 27) {
break;
}
}
cap.release();
}
此处需要补一个结果视频的gif
文件