在Visual Studio上,使用OpenCV实现人脸识别

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 对象来打开相机,然后就可以来操作视频帧了。

我们将项目代码修改为如下内容

cpp 复制代码
#include "OpenCVTest.h"
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main()
{
	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;
		}
	}
	return 0;
}

运行程序,效果如下所示

4. 转化为灰度图像

接下来我们需要将图片转化为灰度图,为什么要进行灰度化处理呢 ? 主要有以下几个作用,提高人脸识别的准确性和可靠性

  • 简化图像处理:灰度化可以将彩色图像转化为黑白图像,使得处理更加简单。彩色图像包含三个通道(红、绿、蓝),而灰度图像只有一个通道,使得处理更加快速和高效。
  • 消除颜色信息:人脸识别对于颜色信息并不是非常敏感,而更关注形状和轮廓等特征。因此,通过灰度化处理,可以消除颜色信息对于后续处理的影响。
  • 提高处理性能:灰度化处理可以减少计算量,提高处理性能。在人脸识别过程中,对每个像素进行颜色计算会消耗大量计算资源,而灰度化处理只需要对每个像素的亮度进行计算,减少了计算量。
  • 突出图像特征:灰度化处理可以突出图像中的边缘和纹理等特征。这些特征对于人脸识别非常关键,可以帮助算法更好地识别人脸。

进行灰度化处理我们需要调用void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 );,这里src是我们输入的图像,dst是我们要输出的图像,code需要传COLOR_BGR2GRAY,表示将BGR转化为灰度图。

要注意,在OpenCV中,是BGR排列方式,而不是RGB排列。

具体完整代码如下

cpp 复制代码
#include "OpenCVTest.h"
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main()
{
	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中是BRG
		imshow("灰度化", grayFrame); //显示灰色图像


		if (waitKey(30) == 27) //ESC键
		{
			break;
		}
	}
	return 0;
}

运行程序,效果如下所示,左边的是彩色画面,右边的是黑白画面

5. 直方图均衡化处理

接着,要进行直方图均衡化处理,为什么要进行这一步操作呢 ? 主要有以下几个作用,提高人脸识别的准确性和可靠性

  • 提高对比度:直方图均衡化通过重新分布图像像素的灰度级,将原始图像中的灰度级分布变得更加均匀。这样做可以增强图像的对比度,使得人脸的特征更加清晰可见。
  • 消除光照变化:人脸识别中的一个挑战是光照变化对人脸图像的影响。直方图均衡化可以消除光照变化,使得人脸图像在不同光照条件下具有一致的亮度和对比度。
  • 提高图像质量:直方图均衡化可以改善图像的质量,去除图像中的噪声和伪影。这对于后续的人脸特征提取和匹配非常重要,可以提高人脸识别的准确性和鲁棒性。
  • 增强细节信息:直方图均衡化可以增强图像的细节信息,使得人脸图像中的纹理和特征更加明显。这对于人脸识别算法的性能至关重要,可以提高人脸识别的准确率和鲁棒性。

直方图均衡化处理需要调用void equalizeHist( InputArray src, OutputArray dst);src是输入的图像,需要是单通道的灰度图,dst是我们输出的图像。

具体完整代码如下

cpp 复制代码
#include "OpenCVTest.h"
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main()
{
	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);


		if (waitKey(30) == 27) //ESC键
		{
			break;
		}
	}
	return 0;
}

运行程序,效果如下所示,最右边的是经过直方图均衡化处理后的

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,需要注意的是,放到代码里,这里的要将\改为/ (或者改为\\也行)。如果不改,那么路径不对,级联分类器会读取出错。

具体代码如下

cpp 复制代码
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;
	}

	//这里省略了原本其他的代码 ...
}

7. 进行人脸检测

接下来我们就要进行人脸检测了,人脸检测需要调用detectMultiScale方法,第一个参数 image 需要传入我们刚才处理后的直方图,第二个参数objects会返回所有检测出来的人脸的坐标。

cpp 复制代码
void detectMultiScale( 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表示画框的颜色。

cpp 复制代码
void rectangle(InputOutputArray img, Rect rec,
				const Scalar& color, int thickness = 1,
				int lineType = LINE_8, int shift = 0);

主要代码如下所示

cpp 复制代码
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)); //在人脸的位置画红色的框
}

来看一下完整代码

kotlin 复制代码
#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;
		}
	}
	return 0;
}

运行程序,来看一下效果

可以看到,人脸已经检测出来了,并对人脸进行了画框。但是可以画面非常的卡顿,因为人脸检测是非常耗时的,可能需要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是一个适配器类,用于将CascadeClassifierDetector接口适配起来,从而用于检测人脸。

再来看一下CascadeDetectorAdapter这个类,里面的detect方法就是用来检测人脸的

8.4 DetectorAgregator

然后来看一下第三行代码中的DetectorAgregator,这里面有tracker = makePtr<DetectionBasedTracker>(mainDetector, trackingDetector, DetectorParams);这行代码是我们需要的,用来传入mainDetectortrackingDetector,生成一个tracker对象。

8.5 开始重新编写代码

这里我们将原来写的人脸检测的代码删除了,代码恢复到了刚配置好OpenCV的初始状态,然后将CascadeDetectorAdapter这个类的代码复制到我们的项目中

cpp 复制代码
class CascadeDetectorAdapter: public DetectionBasedTracker::IDetector
{
public:
    CascadeDetectorAdapter(cv::Ptr<cv::CascadeClassifier> detector):
            IDetector(),
            Detector(detector)
    {
        CV_Assert(detector);
    }

    void detect(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这个对象。

cpp 复制代码
cv::Ptr<DetectionBasedTracker> tracker;

然后创建tracker,并调用run()方法,会启动一个异步线程,后面的人脸检测会在这个异步线程进行检测了。

cpp 复制代码
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);获得识别出来的人脸。

cpp 复制代码
tracker->process(grayFrame);
tracker->getObjects(faces);

核心代码就是如上所示,接下来我们再来看一下完整的代码

cpp 复制代码
#include "OpenCVTest.h"
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

class CascadeDetectorAdapter : public DetectionBasedTracker::IDetector
{
public:
	CascadeDetectorAdapter(cv::Ptr<cv::CascadeClassifier> detector) :
		IDetector(),
		Detector(detector)
	{
		CV_Assert(detector);
	}

	void detect(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;

int main()
{
	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();
	return 0;
}

8.6 运行效果

运行程序,我们就可以看到本文开头给出的效果了

至此,我们就使用OpenCV完成实时人脸跟踪识别了。

9. 本文源码下载

使用OpenCV实现人脸识别示例Demo

相关推荐
却道天凉_好个秋1 天前
OpenCV(四十二):图像分割原理
人工智能·opencv·计算机视觉·图像分割
hero_heart1 天前
opencv和摄影测量坐标系的转换
人工智能·opencv·计算机视觉
却道天凉_好个秋2 天前
OpenCV(四十三):分水岭法
人工智能·opencv·计算机视觉·图像分割·分水岭法
QAQ小菜鸟2 天前
二、Visual Studio 2026如何创建C语言项目
visual studio
编码小哥2 天前
OpenCV几何变换详解:缩放、旋转与平移
人工智能·opencv·计算机视觉
小灰灰搞电子2 天前
Qt 开发环境选择Qt Creator、Visual Studio还是 VS Code?
开发语言·qt·visual studio
whm27772 天前
Visual Basic Data控件
windows·visual studio
游荡的小狼2 天前
Visual Studio Community 2022 安装
visual studio
bkspiderx2 天前
Visual Studio 2026安装常见问题与解决方案
ide·visual studio·vs2026·vc2026安装
bkspiderx2 天前
Visual Studio 2026 安装指南(2025年最新状态+通用流程)
ide·安装·visual studio·vs2026