一、实现目的:
- 了解人脸检测的主要方法;
- 了解 detectMultiScale 函数的功能及用法;
- 掌握使用 OpenCV 提供的分类器和检测器进行人脸检测的方法。
二、实验设备:
- 计算机一台;
- 视觉实验软件环境及资源一套(visual studio 2022,opencv-4.8)。
环境配置请参考以下文章:
- http://t.csdnimg.cn/ku3rS
- http://t.csdnimg.cn/owEsG
- http://t.csdnimg.cn/vEe72
三、实验原理:
人脸检测(Face Detection),就是给一幅图像,找出图像中的所有人脸位置, 通常用一个矩形框框起来,如下图所示。其输入是一幅图像 image,输出是若 干个包含人脸的矩形框位置(x,y,w,h)。人脸检测属于计算机视觉的范畴,早期人 们的主要研究方向是人脸识别,即根据人脸来识别人物的身份,后来在复杂背景 下的人脸检测需求越来越大,人脸检测也逐渐作为一个单独的研究方向发展起来。 目前人脸检测的方法主要有以下两大类。
基于知识的方法:主要利用先验知识将人脸看作器官特征的组合,根据眼睛、 眉毛、嘴巴、鼻子等器官的特征以及相互之间的几何位置关系来检测人脸。主要 包括模板匹配、人脸特征、形状与边缘、纹理特性、颜色特征等方法。
基于统计的方法:将人脸看作一个整体的模式——二维像素矩阵,从统计的 观点通过大量人脸图像样本构造人脸模式空间,根据相似度来判断人脸是否存在。 主要包括主成分分析与特征脸、神经网络方法、支持向量机、隐马尔可夫模型、 Adaboost 算法等。
本次实验将使用Haar+AdaBoost 的算法,实现任意图像中人脸的检测。OpenCV 自带了训练器和检测器,还包含了人脸检测的 XML 文件, 如下图所示。这些文件位于:
D:\software\other\opencv\build\etc\haarcascades
# 其中:D:\software\other是我自己的安装目录
文件夹, 主要保存相关的特征矩阵,以及各个弱分类器相关的信息,可用于检测静止图像、 视频和摄像头所得到图像中的人脸。除此之外还有一个同级文件夹是 lbpcascades, 它不是通过 Haar 特征进行人脸检测,而是采用的 LBP 特征,这里仅作了解,不再细究。
四、算法接口:
CascadeClassifier 是 OpenCV 中用来做目标检测的级联分类器的一个类。早期 OpenCV 版本仅支持 Haar 特征的目标检测,新版本开始支持 LBP 和 HOG 特 征的目标检测。CascadeClassifier 类有一个 detectMultiScale 的成员函数用于调整检测参数,检测目标图像并得到检测结果。
void detectMultiScale( InputArray image,
std::vector<Rect>& objects,
double scaleFactor = 1.1,
int minNeighbors = 3,
int flags = 0,
Size minSize = Size(), Size maxSize = Size());
【image】:输入图像。
【objects】:检测到的目标的矩形框向量组。
【scaleFactor】:在前后两次相继的扫描中搜索窗口的比例系数。默认为 1.1,即每次搜索 窗口依次扩大 10%。
【minNeighbors】:每个目标至少要被检测到不低于该值的次数才算是真的目标。
【flags】:旧版本的 OpenCV 1.x 使用的参数,当前版本已被忽略。
【minSize 和 maxSize】:限制检测到的目标尺寸。
五、实验任务:
1 基础任务:
- 搭建实验环境;
- 根据指导书示例完成人脸检测实验;
- 完成实验报告。
2 拓展任务:
- 调整 minNeighbors 参数的值,观察对人脸检测结果的影响。
- 调整 minSize 和 maxSize 参数的值,观察对人脸检测结果的影响。
- 修改程序,实现在视频流中进行动态的人脸检测。
六、实验内容:
1 搭建实验环境:
见上述推荐的参考文章。
2 人脸检测示例运行:
2.1 人脸识别:
2.1.1 测试代码:
//人脸检测
#include <vector>
#include <opencv2/opencv.hpp>
int main()
{
// 创建级联分类器对象,并加载 OpenCV 自带的人脸分类器
cv::CascadeClassifier faceDetector("D:/software/other/opencv/build/etc/haarcascades/haarcascade_frontalface_alt2.xml");
cv::Mat image = cv::imread("D:/Projects/visio projects/opencv_test1/test_picture/test1.jpg");
// 用于储存检测结果的矩形框向量组
std::vector<cv::Rect> objects;
// 使用分类器进行目标检测,并设置相关参数
faceDetector.detectMultiScale(image, objects, 1.1, 3);
// 在原图上绘制检测得到的矩形框,并显示出来
for (int i = 0; i < objects.size(); ++i) {
cv::rectangle(image, objects[i], cv::Scalar(0, 0, 255));
}
cv::imshow("face_detection", image);
cv::waitKey(0);
return 0;
}
2.1.2 运行结果:
注意这里左下角有个柱子被错误的检测为人脸,后面有用。
2.2 猫脸识别:
2.2.1 测试代码:
//猫脸识别
#include <vector>
#include <opencv2/opencv.hpp>
int main()
{
// 创建级联分类器对象,并加载 OpenCV 自带的猫脸分类器
cv::CascadeClassifier faceDetector("D:/software/other/opencv/build/etc/haarcascades/haarcascade_frontalcatface.xml");
cv::Mat image = cv::imread("D:/Projects/visio projects/opencv_test1/test_picture/test3.jpg");
// 用于储存检测结果的矩形框向量组
std::vector<cv::Rect> objects;
// 使用分类器进行目标检测,并设置相关参数
faceDetector.detectMultiScale(image, objects, 1.1, 3);
// 在原图上绘制检测得到的矩形框,并显示出来
for (int i = 0; i < objects.size(); ++i) {
cv::rectangle(image, objects[i], cv::Scalar(0, 255, 0));
}
cv::imshow("cat_face_detection", image);
cv::waitKey(0);
return 0;
}
2.2.2 运行结果:
3 拓展任务:
(1)调整人脸识别代码中的 minNeighbors 参数的值从3调整为30:
faceDetector.detectMultiScale(image, objects, 1.1, 30);
可以观察到,原来被误识别为人脸的柱子的检测框不见了。
(2)调整猫脸识别代码中的 minSize 和 maxSize 参数的值:
因为测试代码中没有定义minSize 和 maxSize 参数的值,所以我们加入以下代码:
// 调整 minSize 和 maxSize 参数的值
cv::Size minSize(10, 10); // 设置最小尺寸
cv::Size maxSize(50, 50); // 设置最大尺寸
// 使用分类器进行目标检测,并设置相关参数
faceDetector.detectMultiScale(image, objects, 1.1, 3, 0, minSize, maxSize);
新的完整代码如下:
//猫脸识别
#include <vector>
#include <opencv2/opencv.hpp>
int main()
{
// 创建级联分类器对象,并加载 OpenCV 自带的猫脸分类器
cv::CascadeClassifier faceDetector("D:/software/other/opencv/build/etc/haarcascades/haarcascade_frontalcatface.xml");
cv::Mat image = cv::imread("D:/Projects/visio projects/opencv_test1/test_picture/test3.jpg");
// 用于储存检测结果的矩形框向量组
std::vector<cv::Rect> objects;
// 调整 minSize 和 maxSize 参数的值
cv::Size minSize(10, 10); // 设置最小尺寸
cv::Size maxSize(50, 50); // 设置最大尺寸
// 使用分类器进行目标检测,并设置相关参数
faceDetector.detectMultiScale(image, objects, 1.1, 3, 0, minSize, maxSize);
// 在原图上绘制检测得到的矩形框,并显示出来
for (int i = 0; i < objects.size(); ++i) {
cv::rectangle(image, objects[i], cv::Scalar(0, 255, 0));
}
cv::imshow("cat_face_detection", image);
cv::waitKey(0);
return 0;
}
可以看到脸大的猫猫都检测不到了
(3)修改程序,实现在视频流中进行动态的人脸检测:
要在视频流中进行动态人脸检测,需要使用 OpenCV 读取实时视频流(例如,从网络摄像头获取的视频)并对每一帧进行人脸检测,代码如下:
#include <opencv2/opencv.hpp>
#include <vector>
int main()
{
// 创建级联分类器对象,并加载 OpenCV 自带的人脸分类器
cv::CascadeClassifier faceDetector("D:/software/other/opencv/build/etc/haarcascades/haarcascade_frontalface_alt2.xml");
// 创建视频捕获对象,从默认摄像头读取视频
cv::VideoCapture cap(0); // 0 表示默认摄像头
if (!cap.isOpened()) {
std::cerr << "Error: Unable to open camera" << std::endl;
return -1;
}
// 循环读取视频流的每一帧
while (true) {
cv::Mat frame;
cap >> frame; // 获取当前帧
if (frame.empty()) {
std::cerr << "Error: Empty frame" << std::endl;
break;
}
// 用于储存检测结果的矩形框向量组
std::vector<cv::Rect> faces;
// 使用分类器进行人脸检测,调整缩放因子和最小邻近数等参数
faceDetector.detectMultiScale(frame, faces, 1.1, 3, 0, cv::Size(30, 30));
// 在帧上绘制检测得到的矩形框
for (const auto& face : faces) {
cv::rectangle(frame, face, cv::Scalar(0, 0, 255), 2); // 红色矩形框,线宽为 2
}
// 显示带有人脸检测框的当前帧
cv::imshow("Face Detection", frame);
// 检查是否按下 'q' 键退出
if (cv::waitKey(1) == 'q') {
break;
}
}
cap.release(); // 释放摄像头
cv::destroyAllWindows(); // 关闭所有 OpenCV 窗口
return 0;
}
调用的摄像头检测我自己的大脸