这段代码实现了一个使用人脸识别进行登录的MFC(Microsoft Foundation Classes)对话框应用程序。应用程序通过摄像头捕获用户的面部图像,并与存储在本地的面部图像进行比较,以验证用户的身份。具体功能和工作流程如下:
### 主要功能
1. **界面初始化**:
- 加载背景图片和按钮图片。
- 初始化雪花动画效果。
- 启动定时器以更新雪花动画。
2. **人脸识别**:
- 使用 OpenCV 捕捉摄像头视频流。
- 拍摄当前用户的照片并保存为临时文件 `tmp.jpg`。
- 加载本地存储的用户面部图像,并与当前照片进行对比。
- 如果识别成功,播放登录成功的声音,并提取用户信息。
3. **定时器事件**:
- 处理雪花动画。
- 定时刷新摄像头画面,进行拍照和人脸识别。
### 主要流程
1. **初始化对话框**(`OnInitDialog`):
- 加载背景图片和按钮图片。
- 初始化雪花效果并启动定时器。
2. **用户点击登录按钮**(`OnBnClickedButtonLogin`):
- 停止雪花动画定时器。
- 打开摄像头并启动捕捉画面。
- 修改背景为幕布背景。
- 启动定时器以捕捉摄像头画面。
3. **定时器事件处理**(`OnTimer`):
- 更新雪花动画(ID 1)。
- 捕捉摄像头画面并进行人脸识别(ID 2)。
4. **人脸识别(`faceCheck`)**:
- 加载用户目录下的所有面部图像。
- 与当前拍摄的照片进行比较。
- 如果匹配度超过阈值(0.92),返回用户信息。
5. **处理识别结果**:
- 如果识别成功,播放成功声音,提取并保存用户信息。
- 如果识别失败,显示登录失败提示,并重新启动雪花动画定时器。
### 关键函数和代码块
- **`OnInitDialog`**:初始化对话框,加载图片和初始化控件。
- **`DoDataExchange`**:实现控件与数据成员之间的绑定。
- **`OnPaint`**:在对话框上绘制背景图片。
- **`faceCheck`**:进行人脸识别的核心函数。
- **`OnTimer`**:定时器事件处理函数,处理动画更新和人脸识别。
- **`OnBnClickedButtonLogin`**:处理登录按钮点击事件。
- **`OnStnClickedSnow` 和 `OnStnClickedPhotoReal`**:处理静态控件点击事件(未实现具体功能)。
这段代码通过摄像头捕捉用户面部图像,并与存储的面部图像进行比较,以实现人脸识别登录功能。同时,它还包含了定时器事件处理,用于动态更新界面效果(如雪花动画)。
下面是添加了详细注释的代码:
// WinLogin.cpp: 实现文件
#include "pch.h"
#include "MyFaceCheck.h"
#include "afxdialogex.h"
#include "WinLogin.h"
#include "opencv2/opencv.hpp"
#include "tools.h"
#include "mmsystem.h"
#pragma comment(lib, "winmm.lib")
#include "faceTool.h"
#include "WinWelcome.h"
#include <vector>
using namespace std;
extern MHandle faceModel; // 人脸识别模块
using namespace cv;
static VideoCapture cap; // 定义一个摄像头变量
// WinLogin 对话框
IMPLEMENT_DYNAMIC(WinLogin, CDialogEx)
WinLogin::WinLogin(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MYFACECHECK_LOGIN, pParent)
{
}
WinLogin::~WinLogin()
{
}
BOOL WinLogin::OnInitDialog()
{
// 父类的初始化
CDialogEx::OnInitDialog();
// 自己的初始化
LoadPicture(m_imgBG, IDB_PNG4); // 加载背景图片
m_btnMouse.Init(IDB_PNG5, 4, BTN_TYPE_NORMAL); // 初始化登录按钮
CString name;
for (int i = 0; i < 16; i++) {
// 加载雪花图片
name.Format(L"res/snow/snow_%d.bmp", i);
m_snows[i] = (HBITMAP)LoadImage(0, name, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
}
// 启动定时器,用于更新雪花动画
SetTimer(1, 30, NULL);
// 移动雪花背景控件的位置和大小
::MoveWindow(m_snowBG.m_hWnd, 440, 0, 640, 609, 1);
return 0;
}
void WinLogin::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_BUTTON_LOGIN, m_btnMouse); // 绑定登录按钮
DDX_Control(pDX, IDC_SNOW, m_snowBG); // 绑定雪花背景控件
DDX_Control(pDX, IDC_PHOTO_REAL, m_photo); // 绑定摄像头预览控件
}
BEGIN_MESSAGE_MAP(WinLogin, CDialogEx)
ON_WM_PAINT() // 处理绘图消息
ON_WM_TIMER() // 处理定时器消息
ON_BN_CLICKED(IDC_BUTTON_LOGIN, &WinLogin::OnBnClickedButtonLogin) // 处理登录按钮点击事件
ON_STN_CLICKED(IDC_SNOW, &WinLogin::OnStnClickedSnow) // 处理雪花背景点击事件
ON_STN_CLICKED(IDC_PHOTO_REAL, &WinLogin::OnStnClickedPhotoReal) // 处理摄像头预览点击事件
END_MESSAGE_MAP()
// WinLogin 消息处理程序
void WinLogin::OnPaint()
{
// 绘制背景图片
drawPicOnPait(&m_imgBG, this, 0, 0);
}
bool faceCheck(const char* fileName, char* userName)
{
vector<CString> vctPath;
// 查找用户目录下所有.jpg文件
WIN32_FIND_DATA fileData;
HANDLE file = FindFirstFile(L"users/*.jpg", &fileData);
if (file != INVALID_HANDLE_VALUE) {
vctPath.push_back(fileData.cFileName);
while (FindNextFile(file, &fileData)) {
vctPath.push_back(fileData.cFileName);
}
}
// 加载当前拍摄的照片
IplImage* img1 = cvLoadImage(fileName);
for (int i = 0; i < vctPath.size(); i++) {
USES_CONVERSION;
char* othername = T2A(vctPath[i]);
char name[256];
sprintf_s(name, sizeof(name), "users/%s", othername);
IplImage* img2 = cvLoadImage(name);
// 进行人脸比较
float ret = faceCompare(faceModel, img1, img2);
if (ret >= 0.92) {
// 如果匹配成功,提取用户名
string tmpStr = othername;
string nameStr = tmpStr.substr(0, tmpStr.length() - 4); // 去掉.jpg
strcpy(userName, nameStr.c_str());
return true;
}
}
return false;
}
void WinLogin::OnTimer(UINT_PTR nIDEvent)
{
static int snowIndex = 0; // 雪花动画索引
static int count = 0; // 计数器
static char userName[256] = ""; // 用户名
static bool success = false; // 登录成功标志
if (nIDEvent == 1) {
// 更新雪花动画
m_snowBG.SetBitmap(m_snows[snowIndex]);
snowIndex = (snowIndex + 1) % 16;
}
else if (nIDEvent == 2) {
// 刷新摄像头画面
refreshCamera(&cap, m_hWnd, IDC_PHOTO_REAL);
count++;
if (count == 10) {
const char* fileName = "tmp.jpg";
// 拍照并保存
paiZhao(&cap, fileName);
// 人脸识别
if (faceCheck(fileName, userName)) {
// 播放登录成功声音
mciSendString(L"play res/login.mp3", 0, 0, 0);
success = true;
}
}
else if (count == 20) {
count = 0;
KillTimer(2); // 停止定时器
cap.release(); // 释放摄像头
if (success) {
// 登录成功,提取用户信息
strcpy(m_job, strtok(userName, "-"));
strcpy(m_userName, strtok(NULL, "-"));
m_yanZhi = atoi(strtok(NULL, "-"));
m_logined = true;
CDialogEx::OnOK(); // 关闭对话框
}
else {
// 登录失败,显示提示信息
MessageBox(L"登录失败!");
SetTimer(1, 100, NULL); // 重新启动雪花动画定时器
m_logined = false;
}
}
}
CDialogEx::OnTimer(nIDEvent); // 调用父类的定时器处理函数
}
void WinLogin::OnBnClickedButtonLogin()
{
KillTimer(1); // 停止雪花动画定时器
cap.open(0); // 打开摄像头
// 修改雪花背景为幕布背景
HBITMAP pic = (HBITMAP)LoadImage(0, L"res/wall.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
m_snowBG.SetBitmap(pic);
// 调整摄像头预览控件的位置和大小
::MoveWindow(m_photo.m_hWnd, 440, 64, 640, 480, 1);
SetTimer(2, 100, NULL); // 启动定时器以捕捉摄像头画面
}
void WinLogin::OnStnClickedSnow()
{
// 空函数,用于处理雪花背景点击事件
}
void WinLogin::OnStnClickedPhotoReal()
{
// 空函数,用于处理摄像头预览点击事件
}
### 注释内容
1. **头文件和全局变量**:简单介绍包含的头文件和定义的全局变量。
2. **`OnInitDialog` 函数**:对初始化对话框的操作进行详细注释,包括加载图片、初始化控件和启动定时器。
3. **`DoDataExchange` 函数**:注释数据交换绑定的控件。
4. **消息映射表**:说明各消息处理函数的用途。
5. **`OnPaint` 函数**:绘制背景图片。
6. **`faceCheck` 函数**:详细注释人脸识别的流程,包括加载本地图片、进行人脸比较和提取用户名。
7. **`OnTimer` 函数**:注释定时器事件处理的逻辑,包括更新动画和进行人脸识别。
8. **`OnBnClickedButtonLogin` 函数**:注释登录按钮点击后的操作,包括停止动画、打开摄像头和启动定时器。
9. **其他点击事件处理函数**:注释为空函数。
这样,所有关键步骤和函数都有详细的注释,便于阅读和理解代码的功能和流程。