在本教程中,我们将讨论各种人脸检测方法,并对各种方法进行比较。下面是主要的人脸检测方法:

1 OpenCV中的Haar Cascade人脸分类器;

2 OpenCV中的深度学习人脸分类器;

3 Dlib中的hog人脸分类器;

4 Dlib中的深度学习人脸分类器。

Dlib是一个C++工具包(也有python版本),代码地址: http://dlib.net/

本文不涉及任何原理,只讲具体的应用。所有代码模型见:

https://download.csdn.net/download/luohenyj/10997489

https://github.com/luohenyueji/OpenCV-Practical-Exercise

如果没有积分(系统自动设定资源分数)看看参考链接。我搬运过来的,大修改没有。pch是预编译文件。Opencv版本3.4.3以上。

1 OpenCV中的Haar Cascade人脸分类器

基于Haar Cascade的人脸检测器自2001年提出以来,一直是人脸检测领域的研究热点。这种模型和其变种在这里找到:

https://github.com/opencv/opencv/tree/master/data/haarcascades

这种方法优点在CPU上几乎是实时工作的,方法简单可以在不同的尺度上检测人脸。实际就是一个级联分类器,参数可以调整,网上有相关资料。但是不管怎么调整误报率很高,而且人脸框选结果不是那么准确。

代码

C++:

#include "pch.h"
#include "face_detection.h" /**
* @brief 人脸检测haar级联
*
* @param frame 原图
* @param faceCascadePath 模型文件
* @return Mat
*/
Mat detectFaceHaar(Mat frame, string faceCascadePath)
{
//图像缩放
auto inHeight = 300;
auto inWidth = 0;
if (!inWidth)
{
inWidth = (int)(((float)frame.cols / (float)frame.rows) * inHeight);
}
resize(frame, frame, Size(inWidth, inHeight)); //转换为灰度图
Mat frameGray = frame.clone();
//cvtColor(frame, frameGray, CV_BGR2GRAY); //级联分类器
CascadeClassifier faceCascade;
faceCascade.load(faceCascadePath);
std::vector<Rect> faces;
faceCascade.detectMultiScale(frameGray, faces); for (size_t i = 0; i < faces.size(); i++)
{
int x1 = faces[i].x;
int y1 = faces[i].y;
int x2 = faces[i].x + faces[i].width;
int y2 = faces[i].y + faces[i].height;
Rect face_rect(Point2i(x1, y1), Point2i(x2, y2));
rectangle(frameGray, face_rect, cv::Scalar(0, 255, 0), 2, 4);
}
return frameGray;
}

python:

from __future__ import division
import cv2
import time
import sys def detectFaceOpenCVHaar(faceCascade, frame, inHeight=300, inWidth=0):
frameOpenCVHaar = frame.copy()
frameHeight = frameOpenCVHaar.shape[0]
frameWidth = frameOpenCVHaar.shape[1]
if not inWidth:
inWidth = int((frameWidth / frameHeight) * inHeight) scaleHeight = frameHeight / inHeight
scaleWidth = frameWidth / inWidth frameOpenCVHaarSmall = cv2.resize(frameOpenCVHaar, (inWidth, inHeight))
frameGray = cv2.cvtColor(frameOpenCVHaarSmall, cv2.COLOR_BGR2GRAY) faces = faceCascade.detectMultiScale(frameGray)
bboxes = []
for (x, y, w, h) in faces:
x1 = x
y1 = y
x2 = x + w
y2 = y + h
cvRect = [int(x1 * scaleWidth), int(y1 * scaleHeight),
int(x2 * scaleWidth), int(y2 * scaleHeight)]
bboxes.append(cvRect)
cv2.rectangle(frameOpenCVHaar, (cvRect[0], cvRect[1]), (cvRect[2], cvRect[3]), (0, 255, 0),
int(round(frameHeight / 150)), 4)
return frameOpenCVHaar, bboxes if __name__ == "__main__" :
source = 0
if len(sys.argv) > 1:
source = sys.argv[1] faceCascade = cv2.CascadeClassifier('./haarcascade_frontalface_default.xml') cap = cv2.VideoCapture(source)
hasFrame, frame = cap.read() vid_writer = cv2.VideoWriter('output-haar-{}.avi'.format(str(source).split(".")[0]),cv2.VideoWriter_fourcc('M','J','P','G'), 15, (frame.shape[1],frame.shape[0])) frame_count = 0
tt_opencvHaar = 0
while(1):
hasFrame, frame = cap.read()
if not hasFrame:
break
frame_count += 1 t = time.time()
outOpencvHaar, bboxes = detectFaceOpenCVHaar(faceCascade, frame)
tt_opencvHaar += time.time() - t
fpsOpencvHaar = frame_count / tt_opencvHaar label = "OpenCV Haar ; FPS : {:.2f}".format(fpsOpencvHaar)
cv2.putText(outOpencvHaar, label, (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1.4, (0, 0, 255), 3, cv2.LINE_AA) cv2.imshow("Face Detection Comparison", outOpencvHaar) vid_writer.write(outOpencvHaar)
if frame_count == 1:
tt_opencvHaar = 0 k = cv2.waitKey(10)
if k == 27:
break
cv2.destroyAllWindows()
vid_writer.release()

2 OpenCV中的深度学习人脸分类器

OpenCV3.3以上版本就有该分类器的模型。模型来自论文: https://arxiv.org/abs/1512.02325

但是提供了两种不同的模型。一种是16位浮点数的caffe人脸模型(5.4MB),另外一种是8bit量化后的tensorflow人脸模型(2.7MB)。量化是指比如可以用0~255表示原来32个bit所表示的精度,通过牺牲精度来降低每一个权值所需要占用的空间。通常情况深度学习模型会有冗余计算量,冗余性决定了参数个数。因此合理的量化网络也可保证精度的情况下减小模型的存储体积,不会对网络的精度造成影响。具体可以看看深度学习fine-

tuning的论文。通常这种操作可以稍微降低精度,提高速度,大大减少模型体积。

这种方法速度慢了点,但是精度不错。对于调用模型代码写的很清楚。但是tensorflow模型有点小问题,可能只能在opencv3.4.3以上版本通过readNet函数调用。

代码

C++:

#include "pch.h"
#include "face_detection.h" //检测图像宽高
const size_t inWidth = 300;
const size_t inHeight = 300;
//缩放比例
const double inScaleFactor = 1.0;
//阈值
const double confidenceThreshold = 0.7;
//均值
const cv::Scalar meanVal(104.0, 177.0, 123.0); /**
* @brief 人脸检测Opencv ssd
*
* @param frame 原图
* @param configFile 模型结构定义文件
* @param weightFile 模型文件
* @return Mat
*/
Mat detectFaceOpenCVDNN(Mat frame, string configFile, string weightFile)
{
Mat frameOpenCVDNN = frame.clone();
Net net;
Mat inputBlob;
int frameHeight = frameOpenCVDNN.rows;
int frameWidth = frameOpenCVDNN.cols;
//获取文件后缀
string suffixStr = configFile.substr(configFile.find_last_of('.') + 1);
//判断是caffe模型还是tensorflow模型
if (suffixStr == "prototxt")
{
net = dnn::readNetFromCaffe(configFile, weightFile);
inputBlob = cv::dnn::blobFromImage(frameOpenCVDNN, inScaleFactor, cv::Size(inWidth, inHeight), meanVal, false, false);
}
else
{
//bug
//net = dnn::readNetFromTensorflow(configFile, weightFile);
net = dnn::readNet(configFile, weightFile);
inputBlob = cv::dnn::blobFromImage(frameOpenCVDNN, inScaleFactor, cv::Size(inWidth, inHeight), meanVal, true, false);
} //读图检测
net.setInput(inputBlob, "data");
cv::Mat detection = net.forward("detection_out");
cv::Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>()); for (int i = 0; i < detectionMat.rows; i++)
{
//分类精度
float confidence = detectionMat.at<float>(i, 2);
if (confidence > confidenceThreshold)
{
//左上角坐标
int x1 = static_cast<int>(detectionMat.at<float>(i, 3) * frameWidth);
int y1 = static_cast<int>(detectionMat.at<float>(i, 4) * frameHeight);
//右下角坐标
int x2 = static_cast<int>(detectionMat.at<float>(i, 5) * frameWidth);
int y2 = static_cast<int>(detectionMat.at<float>(i, 6) * frameHeight);
//画框
cv::rectangle(frameOpenCVDNN, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(0, 255, 0), 2, 4);
}
}
return frameOpenCVDNN;
}

python

from __future__ import division
import cv2
import time
import sys def detectFaceOpenCVDnn(net, frame):
frameOpencvDnn = frame.copy()
frameHeight = frameOpencvDnn.shape[0]
frameWidth = frameOpencvDnn.shape[1]
blob = cv2.dnn.blobFromImage(frameOpencvDnn, 1.0, (300, 300), [104, 117, 123], False, False) net.setInput(blob)
detections = net.forward()
bboxes = []
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > conf_threshold:
x1 = int(detections[0, 0, i, 3] * frameWidth)
y1 = int(detections[0, 0, i, 4] * frameHeight)
x2 = int(detections[0, 0, i, 5] * frameWidth)
y2 = int(detections[0, 0, i, 6] * frameHeight)
bboxes.append([x1, y1, x2, y2])
cv2.rectangle(frameOpencvDnn, (x1, y1), (x2, y2), (0, 255, 0), int(round(frameHeight/150)), 8)
return frameOpencvDnn, bboxes if __name__ == "__main__" : # OpenCV DNN supports 2 networks.
# 1. FP16 version of the original caffe implementation ( 5.4 MB )
# 2. 8 bit Quantized version using Tensorflow ( 2.7 MB )
DNN = "TF"
if DNN == "CAFFE":
modelFile = "models/res10_300x300_ssd_iter_140000_fp16.caffemodel"
configFile = "models/deploy.prototxt"
net = cv2.dnn.readNetFromCaffe(configFile, modelFile)
else:
modelFile = "models/opencv_face_detector_uint8.pb"
configFile = "models/opencv_face_detector.pbtxt"
net = cv2.dnn.readNetFromTensorflow(modelFile, configFile) conf_threshold = 0.7 source = 0
if len(sys.argv) > 1:
source = sys.argv[1] cap = cv2.VideoCapture(source)
hasFrame, frame = cap.read() vid_writer = cv2.VideoWriter('output-dnn-{}.avi'.format(str(source).split(".")[0]),cv2.VideoWriter_fourcc('M','J','P','G'), 15, (frame.shape[1],frame.shape[0])) frame_count = 0
tt_opencvDnn = 0
while(1):
hasFrame, frame = cap.read()
if not hasFrame:
break
frame_count += 1 t = time.time()
outOpencvDnn, bboxes = detectFaceOpenCVDnn(net,frame)
tt_opencvDnn += time.time() - t
fpsOpencvDnn = frame_count / tt_opencvDnn
label = "OpenCV DNN ; FPS : {:.2f}".format(fpsOpencvDnn)
cv2.putText(outOpencvDnn, label, (10,50), cv2.FONT_HERSHEY_SIMPLEX, 1.4, (0, 0, 255), 3, cv2.LINE_AA) cv2.imshow("Face Detection Comparison", outOpencvDnn) vid_writer.write(outOpencvDnn)
if frame_count == 1:
tt_opencvDnn = 0 k = cv2.waitKey(10)
if k == 27:
break
cv2.destroyAllWindows()
vid_writer.release()

3 Dlib中的hog人脸分类器和Dlib中的hog人脸分类器

Dlib就没有运行了,因为要编译嫌麻烦。而且opencv自带的已经足够了。Dlib里面人脸分类器调用和opencv一样。

Dlib所用的人脸数据见:

Hog(2825张图像):

http://dlib.net/files/data/dlib_face_detector_training_data.tar.gz

dnn(7220张图像):

http://dlib.net/files/data/dlib_face_detection_dataset-2016-09-30.tar.gz

4 方法比较

OpencvDNN综合来说是最好的方法。不过要opencv3.43以上,对尺寸要求不高,速度精度都不错。如果追求高精度用caffe模型就行了,opencv3.4.1以上就可以了。OpenCV

DNN低版本对tensorflow模型支持不好。

Dlib Hog在CPU下,检测速度最快但是小图像(人脸像素70以下)是无效的。因此第二个推荐是Hog。

Dlib DNN在GPU下,应该是最好的选择,精度都是最高的,但是有点慢。

Haar Cascade不推荐太古老了,而且错误率很高。

参考

https://www.learnopencv.com/face-detection-opencv-dlib-and-deep-learning-c-python/

[OpenCV实战]2 人脸识别算法对比的更多相关文章

  1. arcface和Dlib人脸识别算法对比

    我司最近要做和人脸识别相关的产品,原来使用的是其他的在线平台,识别率和识别速度很满意,但是随着量起来的话,成本也是越来越不能接受(目前该功能我们是免费给用户使用的),而且一旦我们的设备掉线了就无法使用 ...

  2. opencv学习之路(40)、人脸识别算法——EigenFace、FisherFace、LBPH

    一.人脸识别算法之特征脸方法(Eigenface) 1.原理介绍及数据收集 特征脸方法主要是基于PCA降维实现. 详细介绍和主要思想可以参考 http://blog.csdn.net/u0100066 ...

  3. 【从零学习openCV】IOS7人脸识别实战

    前言 接着上篇<IOS7下的人脸检測>,我们顺藤摸瓜的学习怎样在IOS7下用openCV的进行人脸识别,实际上非常easy,因为人脸检測部分已经完毕,剩下的无非调用openCV的方法对採集 ...

  4. OpenCV 和 Dlib 人脸识别基础

    00 环境配置 Anaconda 安装 1 下载 https://repo.anaconda.com/archive/ 考虑到兼容性问题,推荐下载Anaconda3-5.2.0版本. 2 安装 3 测 ...

  5. OpenCV实战:人脸关键点检测(FaceMark)

    Summary:利用OpenCV中的LBF算法进行人脸关键点检测(Facial Landmark Detection) Author:    Amusi Date:       2018-03-20 ...

  6. OpenCV学习(36) 人脸识别(1)

    本文主要参考OpenCV人脸识别教程:http://docs.opencv.org/modules/contrib/doc/facerec/facerec_tutorial.html 1.OpenCV ...

  7. Eigenface与PCA人脸识别算法实验

    简单的特征脸识别实验 实现特征脸的过程其实就是主成分分析(Principal Component Analysis,PCA)的一个过程.关于PCA的原理问题,它是一种数学降维的方法.是为了简化问题.在 ...

  8. DeepID人脸识别算法之三代(转)

    DeepID人脸识别算法之三代 转载请注明:http://blog.csdn.net/stdcoutzyx/article/details/42091205 DeepID,目前最强人脸识别算法,已经三 ...

  9. 基于MATLAB的人脸识别算法的研究

    基于MATLAB的人脸识别算法的研究 作者:lee神 现如今机器视觉越来越盛行,从智能交通系统的车辆识别,车牌识别到交通标牌的识别:从智能手机的人脸识别的性别识别:如今无人驾驶汽车更是应用了大量的机器 ...

随机推荐

  1. 基于纯前端类Excel表格控件实现在线损益表应用

    财务报表也称对外会计报表,是会计主体对外提供的反映企业或预算单位一定时期资金.利润状况的会计报表,由资产负债表.损益表.现金流量表或财务状况变动表.附表和附注构成.财务报表是财务报告的主要部分,不包括 ...

  2. 【算法训练营day4】LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表II

    [算法训练营day4]LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表 ...

  3. Linux系统管理_用户管理

    cat /etc/passwd #账户文件 cat /etc/shadow #密码文件 cat /etc/login.defs #密码策略机UID定义文件 #普通用户UID范围1000~60000:系 ...

  4. 『现学现忘』Git分支 — 40、分支基本操作(一)

    目录 1.创建分支 (1)创建分支 (2)图示理解 2.查看分支列表 3.分支切换 4.查看所有分支的最后一个提交 5.删除分支 1.创建分支 (1)创建分支 Git 是怎么创建新分支的呢? 很简单, ...

  5. C语言整人关机程序

    #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char input ...

  6. python实现多接口翻译软件

    本实验用pyqt5做了一个小软件,里面使用了市面上主流的6种翻译软件接口:谷歌.百度.有道.金山词霸.腾讯.必应,界面如图所示: 以下是程序代码: import time,sys,os,hashlib ...

  7. DQL-聚合函数

    DQL-聚合函数 SQL基本函数,聚合函数对一组值执行计算,并返回单个值,也被称为组函数. 聚合函数对一组值执行计算并返回单一的值.除 COUNT 以外,聚合函数忽略空值,如果COUNT函数的应用对象 ...

  8. 要写文档了,emmm,先写个文档工具吧——DocMarkdown

    前言 之前想用Markdown来写框架文档,找来找去发现还是Jekyll的多,但又感觉不是很合我的需求 于是打算自己简单弄一个展示Markdown文档的网站工具,要支持多版本.多语言.导航.页内导航等 ...

  9. KeeWiDB的高性能修炼之路:架构篇

    数据也有冷热之分,你知道吗? 根据访问的频率的高低可将数据分为热数据和冷数据,访问频率高的则为热数据,低为冷数据.如果热.冷数据不区分,一并存储,显然不科学.将冷数据也存储在昂贵的内存中,那么你想,成 ...

  10. Docker | 常用命令——排错很有帮助

    众所周知,docker 排查问题相较而言是困难的.因此,熟知一些常用命令对我们快速的排查定位问题是非常有帮助的.下面让我们一起来学习一下吧 1.显示docker的系统信息 docker info [r ...