OpenCV3.3之后的版本都支持了深度学习框架,具体的模型相关路径如下图所示
一、相关文件下载
前期准备工作:
下载两个文件:下载链接
最好放到跟项目一块,到时候方便调用
二、代码
yy_main.cpp
pbtxt_path
和pb_path
为下载的那俩文件的路径
cv::dnn::Net net = cv::dnn::readNetFromTensorflow(pb_path, pbtxt_path);
读取得到神经网络对象net
VideoCapture capture(0);
,这是调用本地摄像头,要是播放视频,可以将参数0换成视频的路径字符串即可,路径记得是正斜杠哈
cv::Mat frame;
用于存储视频中的每一帧数据信息
capture.read(frame);
读取每一帧数据信息
flip(frame,frame,1);
如果是调用本地摄像头的话,可能需要对视频内容进行翻转一下,摄像头详细内容可参考博文:二十二、摄像头的调用
cv::Mat blob = cv::dnn::blobFromImage(frame, 1.0, Size(300, 300), cv::Scalar(104, 177, 123), false, false);
blobFromImage主要是用来对图片进行预处理
参数一:frame
,将要输入神经网络进行处理的图片
参数二:1.0
,该参数得优先考虑到参数四,当我们将图片减去平均值之后,还可以对剩下的像素值进行一定的尺度缩放,默认值是1.0;若需要减去平均像素之后的值,全部缩小一半,那么可以将该参数设为1/2即可
参数三:Size(300, 300)
,训练时,输入的图片大小
参数四:cv::Scalar(104, 177, 123)
,对BGR通道的像素求一个平均值,然后将每个像素值减去平均值,得到像素之间的相对值,尽可能减少光照等因素的影响;OpenCV给出了均值,如下图所示,当然OpenCV给出的肯定是按照BGR顺序来的
参数五:OpenCV中图片通道顺序是BGR,但是平均值假设的顺序是RGB,所以如果需要交换R和G,那么就要设置为true;这里是通过查阅OpenCV给出的均值进行填入的,故也是BGR顺序,不需要交换
参数六:是否对图片进行裁剪,这里就是false即可
下面就是参数四的设置由来
net.setInput(blob);
把网络传进去
cv::Mat probs = net.forward();
反向传播求梯度求结果,得到的返回结果为一个个的box,大小为1×1×N×7
,N代表有多少张人脸,每个人脸有7个值;7个值得前两个不需要解析,第三个值是置信度,后四个值是左上角和右下角得坐标,后期需要这些坐标来进行框人脸
Mat detectMat(probs.size[2], probs.size[3], CV_32F, probs.ptr<float>());
人脸信息是四维,解析人脸数据信息
detectMat.rows
,共有7行,每一行都是float数据
float conf = detectMat.at<float>(row, 2);
第三个是置信度,获取到置信度,置信度超过多少就算是人脸,根据情况而定
float x1 = detectMat.at<float>(row, 3) * frame.cols;
float y1 = detectMat.at<float>(row, 4) * frame.rows;
float x2 = detectMat.at<float>(row, 5) * frame.cols;
float y2 = detectMat.at<float>(row, 6) * frame.rows;
得到左上角(x1,y1)和右下角(x2,y2)坐标
因为预测得到得detectMat.at<float>(row, 3)
是0-1之间的,目前是在图片上进行显示,故再乘以对应的每帧图像的行或列即可,例如这个是乘以原图的列frame.cols
,得到实际人脸左上角关键点在图片中的实际的x位置,其他也都类似
cv::Rect box(x1, y1, x2 - x1, y2 - y1);
定义进行对象,参数分别为起始坐标和宽高信息
cv::rectangle(frame, box, cv::Scalar(255, 0, 0), 2, 8);
绘制矩形框,具体可参考博文:十四、图像几何形状绘制
#include<opencv2/opencv.hpp>
#include<iostream>using namespace cv;int main(int argc, char** agrv) {std::string pbtxt_path = "E:/C++_workspace/finally_face/opencv_face_detector.pbtxt";std::string pb_path = "E:/C++_workspace/finally_face/opencv_face_detector_uint8.pb";cv::dnn::Net net = cv::dnn::readNetFromTensorflow(pb_path, pbtxt_path);VideoCapture capture(0);cv::Mat frame;while (true) {capture.read(frame);//也可以通过之前的滤波等操作实现美颜效果//其实本质都是对每帧图像frame进行处理的而已//GaussianBlur(frame, frame, Size(3, 3), 1, 1);flip(frame,frame,1);//左右翻转if (frame.empty()) {break;}cv::Mat blob = cv::dnn::blobFromImage(frame, 1.0, Size(300, 300), cv::Scalar(104, 177, 123), false, false);net.setInput(blob);cv::Mat probs = net.forward();//1*1*N*7Mat detectMat(probs.size[2], probs.size[3], CV_32F, probs.ptr<float>());for (int row = 0; row < detectMat.rows; row++) {float conf = detectMat.at<float>(row, 2);if (conf > 0.5) {float x1 = detectMat.at<float>(row, 3) * frame.cols;float y1 = detectMat.at<float>(row, 4) * frame.rows;float x2 = detectMat.at<float>(row, 5) * frame.cols;float y2 = detectMat.at<float>(row, 6) * frame.rows;cv::Rect box(x1, y1, x2 - x1, y2 - y1);cv::rectangle(frame, box, cv::Scalar(255, 0, 0), 2, 8);}}cv::imshow("frame", frame);int c = waitKey(10);if (c == 27) {break;}}cv::waitKey(0);capture.release();cv::destroyAllWindows();return 0;
}
项目结构如下:
运行效果如下: