1. 环境与说明
本文介绍了如何在
Visual Studio
上,使用
OpenCV
来实现人脸识别的功能
环境说明 :
- 操作系统 :
windows 10 64位
Visual Studio
版本 :Visual Studio Community 2022 (社区版)
OpenCV
版本 :OpenCV-4.8.0 (2023年7月最新版)
实现效果如图所示,识别到的人脸会用红框框出来 :
2. 配置Visual Studio环境
这部分详见我的另一篇博客 : Visual Studio 2022 cmake配置opencv开发环境
最终配置好后,能够在
Visual Studio
中正常调用
OpenCV
,运行
CMake
项目(
C++
程序)
3. 实现摄像头预览
这部分要用到
VideoCapture
这个类,
VideoCapture
既支持从视频文件读取,也支持直接从摄像机等监控器中读取,还可以读取
IP
视频流,要想获取视频需要先创建一个
VideoCapture
对象来打开相机,然后就可以来操作视频帧了。
我们将项目代码修改为如下内容
#include"OpenCVTest.h"#include"opencv2/opencv.hpp"usingnamespace std;usingnamespace cv;intmain(){
VideoCapture capture;//打开相机,这个传入的相机ID为0
capture.open(0);if(!capture.isOpened()){
cout <<"opencv打开摄像头失败!\n"<< endl;return-1;}//Mat矩阵,用来存一张图片
Mat frame;while(true){//从capture中取数据,将画面输出到frame矩阵里面
capture >> frame;if(frame.empty()){
cout <<"读取摄像头数据失败\n"<< endl;}imshow("摄像头", frame);//显示图像if(waitKey(30)==27)//按下ESC键退出程序{break;}}return0;}
运行程序,效果如下所示
4. 转化为灰度图像
接下来我们需要将图片转化为灰度图,为什么要进行灰度化处理呢 ? 主要有以下几个作用,提高人脸识别的准确性和可靠性
- 简化图像处理:灰度化可以将彩色图像转化为黑白图像,使得处理更加简单。彩色图像包含三个通道(红、绿、蓝),而灰度图像只有一个通道,使得处理更加快速和高效。
- 消除颜色信息:人脸识别对于颜色信息并不是非常敏感,而更关注形状和轮廓等特征。因此,通过灰度化处理,可以消除颜色信息对于后续处理的影响。
- 提高处理性能:灰度化处理可以减少计算量,提高处理性能。在人脸识别过程中,对每个像素进行颜色计算会消耗大量计算资源,而灰度化处理只需要对每个像素的亮度进行计算,减少了计算量。
- 突出图像特征:灰度化处理可以突出图像中的边缘和纹理等特征。这些特征对于人脸识别非常关键,可以帮助算法更好地识别人脸。
进行灰度化处理我们需要调用
void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 );
,这里
src
是我们输入的图像,
dst
是我们要输出的图像,
code
需要传
COLOR_BGR2GRAY
,表示将
BGR
转化为灰度图。
要注意,在
OpenCV
中,是
BGR
排列方式,而不是
RGB
排列。
具体完整代码如下
#include"OpenCVTest.h"#include"opencv2/opencv.hpp"usingnamespace std;usingnamespace cv;intmain(){
VideoCapture capture;
capture.open(0);if(!capture.isOpened()){
cout <<"opencv打开摄像头失败!\n"<< endl;return-1;}
Mat frame;//摄像头彩色图像
Mat grayFrame;//摄像头灰度图像while(true){//从capture中取数据,将画面输出到frame矩阵里面
capture >> frame;if(frame.empty()){
cout <<"读取摄像头数据失败!\n"<< endl;return-1;}imshow("摄像头", frame);//显示彩色图像//灰度化处理cvtColor(frame, grayFrame, COLOR_BGR2GRAY);//注意 : OpenCV中是BRGimshow("灰度化", grayFrame);//显示灰色图像if(waitKey(30)==27)//ESC键{break;}}return0;}
运行程序,效果如下所示,左边的是彩色画面,右边的是黑白画面
5. 直方图均衡化处理
接着,要进行直方图均衡化处理,为什么要进行这一步操作呢 ? 主要有以下几个作用,提高人脸识别的准确性和可靠性
- 提高对比度:直方图均衡化通过重新分布图像像素的灰度级,将原始图像中的灰度级分布变得更加均匀。这样做可以增强图像的对比度,使得人脸的特征更加清晰可见。
- 消除光照变化:人脸识别中的一个挑战是光照变化对人脸图像的影响。直方图均衡化可以消除光照变化,使得人脸图像在不同光照条件下具有一致的亮度和对比度。
- 提高图像质量:直方图均衡化可以改善图像的质量,去除图像中的噪声和伪影。这对于后续的人脸特征提取和匹配非常重要,可以提高人脸识别的准确性和鲁棒性。
- 增强细节信息:直方图均衡化可以增强图像的细节信息,使得人脸图像中的纹理和特征更加明显。这对于人脸识别算法的性能至关重要,可以提高人脸识别的准确率和鲁棒性。
直方图均衡化处理需要调用
void equalizeHist( InputArray src, OutputArray dst);
,
src
是输入的图像,需要是单通道的灰度图,
dst
是我们输出的图像。
具体完整代码如下
#include"OpenCVTest.h"#include"opencv2/opencv.hpp"usingnamespace std;usingnamespace cv;intmain(){
VideoCapture capture;
capture.open(0);if(!capture.isOpened()){
cout <<"opencv打开摄像头失败!\n"<< endl;return-1;}
Mat frame;//摄像头彩色图像
Mat grayFrame;//摄像头灰度图像
Mat equalizeFrame;//直方图while(true){
capture >> frame;//从capture中取数据,将画面输出到frame矩阵里面if(frame.empty()){
cout <<"读取摄像头数据失败!\n"<< endl;return-1;}imshow("摄像头", frame);//显示图像//灰度化处理cvtColor(frame, grayFrame, COLOR_BGR2GRAY);//注意 : OpenCV中是BRGimshow("灰度化", grayFrame);//显示图像//直方图均衡化,用来增强图像对比度,从而让轮廓更加明显equalizeHist(grayFrame, equalizeFrame);imshow("直方图", equalizeFrame);if(waitKey(30)==27)//ESC键{break;}}return0;}
运行程序,效果如下所示,最右边的是经过直方图均衡化处理后的
6. 加载级联分类器
级联分类器
CascadeClassifier
的作用是进行目标检测。它是一种基于机器学习的分类器,通过训练多个弱分类器来识别目标物体。这些弱分类器层层级联,形成一个级联分类器,能够快速准确地检测出图像中的目标物体。
级联分类器通常用于人脸检测,可以通过训练来识别人的面部特征,如眼睛、鼻子、嘴巴等,从而识别人脸并定位人脸的位置。在
OpenCV
中,
CascadeClassifier
类提供了一个方便的接口,可以加载预训练的级联分类器,并进行目标检测操作。
首先我们要去加载级联分类器文件(
xml
文件),这些文件位于
D:\Developer\opencv4.8.0\opencv\build\etc
目录下,这里我们用的是
haarcascade
这种基于梯度提升决策树的分类器 (另一种
lbpcascade
是一种基于局部二值模式
LBP
的分类器)
在
haarcascade
目录下,我们可以看到
haarcascade_frontalface_alt.xml
这个文件,就是我们需要的,用于人脸识别的级联分类器了。
所以,我们加载级联分类器的时候,去指定这个路径
D:\Developer\opencv4.8.0\opencv\build\etc\haarcascades\haarcascade_frontalface_alt.xml
,需要注意的是,放到代码里,这里的要将
\
改为
/
(或者改为
\\
也行)。如果不改,那么路径不对,级联分类器会读取出错。
具体代码如下
intmain(){
CascadeClassifier face_CascadeClassifier;if(!face_CascadeClassifier.load("D:/Developer/opencv4.8.0/opencv/build/etc/haarcascades/haarcascade_frontalface_alt.xml")){
cout <<"级联分类器加载失败!\n"<< endl;return-1;}//这里省略了原本其他的代码 ...}
7. 进行人脸检测
接下来我们就要进行人脸检测了,人脸检测需要调用
detectMultiScale
方法,第一个参数
image
需要传入我们刚才处理后的直方图,第二个参数
objects
会返回所有检测出来的人脸的坐标。
voiddetectMultiScale( InputArray image,
CV_OUT std::vector<Rect>& objects,double scaleFactor =1.1,int minNeighbors =3,int flags =0,
Size minSize =Size(),
Size maxSize =Size());
还有一个
rectangle
方法,用来在得到人脸坐标之后,进行画框。第一个参数
img
代表要在哪个图像上画框,第二个参数
rec
表示框的坐标,第三个参数
color
表示画框的颜色。
voidrectangle(InputOutputArray img, Rect rec,const Scalar& color,int thickness =1,int lineType = LINE_8,int shift =0);
主要代码如下所示
std::vector<Rect> faces;
face_CascadeClassifier.detectMultiScale(grayFrame, faces);//检测人脸for(size_t i =0; i < faces.size(); i++){rectangle(frame,faces[i],Scalar(0,0,255));//在人脸的位置画红色的框}
来看一下完整代码
#include "OpenCVTest.h"
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(){//加载级联分类器
CascadeClassifier face_CascadeClassifier;if(!face_CascadeClassifier.load("D:/Developer/opencv4.8.0/opencv/build/etc/haarcascades/haarcascade_frontalface_alt.xml")){
cout <<"级联分类器加载失败!\n"<< endl;return-1;}
VideoCapture capture;
capture.open(0);if(!capture.isOpened()){
cout <<"opencv打开摄像头失败!\n"<< endl;return-1;}
Mat frame;//摄像头彩色图像
Mat grayFrame;//摄像头灰度图像
Mat equalizeFrame;//直方图while(true){
capture >> frame;//从capture中取数据,将画面输出到frame矩阵里面if(frame.empty()){
cout <<"读取摄像头数据失败!\n"<< endl;}//imshow("摄像头", frame); //显示图像//灰度化处理cvtColor(frame, grayFrame, COLOR_BGR2GRAY);//注意 : OpenCV中是BRG//imshow("灰度化", grayFrame); //显示图像//直方图均衡化,用来增强图像对比度,从而让轮廓更加明显equalizeHist(grayFrame, equalizeFrame);//imshow("直方图", equalizeFrame);
std::vector<Rect> faces;
face_CascadeClassifier.detectMultiScale(grayFrame, faces);//检测人脸for(size_t i =0; i < faces.size(); i++){rectangle(frame,faces[i],Scalar(0,0,255));}imshow("摄像头", frame);//显示图像if(waitKey(30)==27)//ESC键{break;}}return0;}
运行程序,来看一下效果
可以看到,人脸已经检测出来了,并对人脸进行了画框。但是可以画面非常的卡顿,因为人脸检测是非常耗时的,可能需要500毫秒甚至1-2秒时间,这里每一帧都去检测人脸,导致了异常卡顿。所以这种方式只适合用来检测静态图像,并不适合用作实时的摄像头人脸跟踪检测。
8. 实现实时人脸跟踪检测
8.1 OpenCV Android Demo
那我们需要来怎么做呢 ? 其实我们可以来看一下官方的示例,我们要去下载官方的Android包,里面有Android的官方示例。
8.2 DetectionBasedTracker_jni.cpp
我们下载解压后,可以在
OpenCV-android-sdk\samples\face-detection\jni
目录下找到
DetectionBasedTracker_jni.cpp
这个文件
在里面的
nativeCreateObject
方法里,我们可以发现其调用了这几句代码
8.3 CascadeDetectorAdapter
CascadeDetectorAdapter
是一个适配器类,用于将
CascadeClassifier
与
Detector
接口适配起来,从而用于检测人脸。
再来看一下
CascadeDetectorAdapter
这个类,里面的
detect
方法就是用来检测人脸的
8.4 DetectorAgregator
然后来看一下第三行代码中的
DetectorAgregator
,这里面有
tracker = makePtr<DetectionBasedTracker>(mainDetector, trackingDetector, DetectorParams);
这行代码是我们需要的,用来传入
mainDetector
和
trackingDetector
,生成一个
tracker
对象。
8.5 开始重新编写代码
这里我们将原来写的人脸检测的代码删除了,代码恢复到了刚配置好OpenCV的初始状态,然后将
CascadeDetectorAdapter
这个类的代码复制到我们的项目中
classCascadeDetectorAdapter:public DetectionBasedTracker::IDetector{public:CascadeDetectorAdapter(cv::Ptr<cv::CascadeClassifier> detector):IDetector(),Detector(detector){CV_Assert(detector);}voiddetect(const cv::Mat &Image, std::vector<cv::Rect>&objects){
Detector->detectMultiScale(Image, objects, scaleFactor, minNeighbours,0, minObjSize, maxObjSize);}virtual~CascadeDetectorAdapter(){}private:CascadeDetectorAdapter();
cv::Ptr<cv::CascadeClassifier> Detector;};
声明
tracker
这个对象。
cv::Ptr<DetectionBasedTracker> tracker;
然后创建
tracker
,并调用
run()
方法,会启动一个异步线程,后面的人脸检测会在这个异步线程进行检测了。
string stdFileName ="D:/Developer/opencv4.8.0/opencv/build/etc/haarcascades/haarcascade_frontalface_alt.xml";//创建一个主检测适配器
cv::Ptr<CascadeDetectorAdapter> mainDetector =makePtr<CascadeDetectorAdapter>(makePtr<CascadeClassifier>(stdFileName));//创建一个跟踪检测适配器
cv::Ptr<CascadeDetectorAdapter> trackingDetector =makePtr<CascadeDetectorAdapter>(makePtr<CascadeClassifier>(stdFileName));//创建跟踪器
DetectionBasedTracker::Parameters DetectorParams;
tracker =makePtr<DetectionBasedTracker>(mainDetector, trackingDetector, DetectorParams);
tracker->run();
然后在人脸检测的使用调用
tracker->process(grayFrame);
进行人脸检测,并调用
tracker->getObjects(faces);
获得识别出来的人脸。
tracker->process(grayFrame);
tracker->getObjects(faces);
核心代码就是如上所示,接下来我们再来看一下完整的代码
#include"OpenCVTest.h"#include"opencv2/opencv.hpp"usingnamespace std;usingnamespace cv;classCascadeDetectorAdapter:public DetectionBasedTracker::IDetector{public:CascadeDetectorAdapter(cv::Ptr<cv::CascadeClassifier> detector):IDetector(),Detector(detector){CV_Assert(detector);}voiddetect(const cv::Mat& Image, std::vector<cv::Rect>& objects){
Detector->detectMultiScale(Image, objects, scaleFactor, minNeighbours,0, minObjSize, maxObjSize);}virtual~CascadeDetectorAdapter(){}private:CascadeDetectorAdapter();
cv::Ptr<cv::CascadeClassifier> Detector;};
cv::Ptr<DetectionBasedTracker> tracker;intmain(){
string stdFileName ="D:/Developer/opencv4.8.0/opencv/build/etc/haarcascades/haarcascade_frontalface_alt.xml";//创建一个主检测适配器
cv::Ptr<CascadeDetectorAdapter> mainDetector =makePtr<CascadeDetectorAdapter>(makePtr<CascadeClassifier>(stdFileName));//创建一个跟踪检测适配器
cv::Ptr<CascadeDetectorAdapter> trackingDetector =makePtr<CascadeDetectorAdapter>(makePtr<CascadeClassifier>(stdFileName));//创建跟踪器
DetectionBasedTracker::Parameters DetectorParams;
tracker =makePtr<DetectionBasedTracker>(mainDetector, trackingDetector, DetectorParams);
tracker->run();
VideoCapture capture;
capture.open(0);if(!capture.isOpened()){
cout <<"opencv打开摄像头失败!\n"<< endl;return-1;}
Mat frame;//摄像头彩色图像
Mat grayFrame;//摄像头灰度图像
Mat equalizeFrame;//直方图while(true){
capture >> frame;//从capture中取数据,将画面输出到frame矩阵里面if(frame.empty()){
cout <<"读取摄像头数据失败!\n"<< endl;return-1;}//imshow("摄像头", frame); //显示图像//灰度化处理cvtColor(frame, grayFrame, COLOR_BGR2GRAY);//注意 : OpenCV中是BRG//imshow("灰度化", grayFrame); //显示图像//直方图均衡化,用来增强图像对比度,从而让轮廓更加明显equalizeHist(grayFrame, equalizeFrame);//imshow("直方图", equalizeFrame);
std::vector<Rect> faces;
tracker->process(grayFrame);
tracker->getObjects(faces);for(size_t i =0; i < faces.size(); i++){rectangle(frame, faces[i],Scalar(0,0,255));}imshow("摄像头", frame);//显示图像if(waitKey(30)==27)//ESC键{break;}}
tracker->stop();return0;}
8.6 运行效果
运行程序,我们就可以看到本文开头给出的效果了
至此,我们就使用
OpenCV
完成实时人脸跟踪识别了。
9. 本文源码下载
使用OpenCV实现人脸识别示例Demo
版权归原作者 氦客 所有, 如有侵权,请联系我们删除。