一、项目概述
1.1 项目目标和用途
本项目旨在基于嵌入式STM32MP157开发板,搭建一个系统软硬件开发平台,以Linux操作系统为基础,研发一个完整的人脸识别系统。该系统可以用于安防监控、考勤管理等应用场景,实现对人脸的实时检测与识别。
1.2 技术栈关键词
-
嵌入式开发
-
STM32MP157
-
Linux
-
Qt
-
OpenCV
-
V4L2
-
AdaBoost
-
LBP特征
二、系统架构
2.1 设计系统架构
本项目系统架构分为宿主机与目标机两部分,宿主机用于开发和调试,目标机则负责运行人脸识别功能。以下是系统架构的设计思路:
-
宿主机:运行Linux操作系统,负责Qt环境搭建与OpenCV库的编译和移植。
-
目标机:运行嵌入式Linux,负责人脸识别的实时处理。
2.2 选择合适的单片机和技术栈
-
单片机:STM32MP157
-
通信协议:使用串口和网络协议进行宿主机与目标机的通信。
-
传感器:USB摄像头(支持V4L2)
-
无线通信模块:Wi-Fi模块(用于数据传输)
2.3 系统架构图
开发 编译 调试 图像采集 人脸识别 宿主机 Qt环境 OpenCV库 目标机 USB摄像头 识别结果
三、环境搭建和注意事项
3.1 开发环境搭建
- 宿主机环境安装:
-
安装Ubuntu 20.04或更高版本的Linux操作系统。
-
使用apt命令安装Qt和OpenCV:
bashsudo apt-get install qt5-default libopencv-dev
- 目标机环境安装:
-
下载源码并编译:
-
在STM32MP157上安装Linux系统(建议使用Yocto)。
-
编译并安装Qt和OpenCV:
bashgit clone https://code.qt.io/qt/qt5.git git clone https://github.com/opencv/opencv.git
3.2 注意事项
-
确保开发板与宿主机之间的网络连接正常。
-
在编译过程中,注意依赖库的版本兼容性。
四、代码实现过程
本节将详细介绍人脸识别系统中各功能模块的实现过程,包括图像采集模块、基于AdaBoost的人脸检测模块、人脸图像预处理模块和基于LBP特征的人脸识别模块。每个模块的实现将提供代码示例、逻辑解释和时序图,以便清晰展示各模块之间的连接关系。
4.1 图像采集模块(基于V4L2)
4.1.1 模块概述
该模块负责从USB摄像头实时采集图像数据,并将获取的图像传递给后续的人脸检测模块。我们使用OpenCV库来简化图像处理过程,并通过Video4Linux2(V4L2)接口访问摄像头。
4.1.2 代码实现
c
#include <opencv2/opencv.hpp>
#include <iostream>
class ImageCapture {
public:
ImageCapture() {
// 构造函数,初始化摄像头
cap.open(0); // 0表示默认摄像头
if (!cap.isOpened()) {
std::cerr << "Error: Could not open camera." << std::endl;
exit(EXIT_FAILURE);
}
}
cv::Mat getFrame() {
cv::Mat frame;
cap >> frame; // 从摄像头读取图像
return frame;
}
private:
cv::VideoCapture cap; // OpenCV的视频捕获对象
};
// 使用示例
int main() {
ImageCapture imageCapture;
while (true) {
cv::Mat frame = imageCapture.getFrame(); // 获取图像
cv::imshow("Captured Image", frame); // 显示图像
if (cv::waitKey(30) >= 0) break; // 按任意键退出
}
return 0;
}
4.1.3 逻辑解释
-
初始化摄像头:在
ImageCapture
类的构造函数中,通过cv::VideoCapture
对象打开默认摄像头(设备编号为0)。如果摄像头打开失败,程序会输出错误信息并终止运行。 -
获取图像:
getFrame
方法调用cap >> frame
从摄像头获取当前帧图像并返回该图像。这一过程是实时进行的,确保系统能够持续地接收图像数据。 -
图像显示:在主函数中,使用
cv::imshow
函数实时显示捕获的图像。当用户按下任意键时,循环将结束,程序将退出。
4.1.4 时序图
User Camera OpenCV 请求图像 发送图像 显示图像 User Camera OpenCV
4.2 人脸检测模块(基于AdaBoost)
4.2.1 模块概述
本模块利用OpenCV提供的Haar特征分类器基于AdaBoost算法对图像中的人脸进行检测。该模块接收图像输入,输出检测到的人脸区域,以便后续处理。
4.2.2 代码实现
c
#include <opencv2/opencv.hpp>
#include <iostream>
class FaceDetector {
public:
FaceDetector() {
// 加载Haar特征分类器
if (!faceCascade.load("haarcascade_frontalface_alt.xml")) {
std::cerr << "Error: Could not load classifier." << std::endl;
exit(EXIT_FAILURE); // 如果加载失败,则终止程序
}
}
std::vector<cv::Rect> detectFaces(const cv::Mat& frame) {
std::vector<cv::Rect> faces;
// 使用Haar分类器检测人脸
faceCascade.detectMultiScale(frame, faces, 1.1, 3, 0, cv::Size(30, 30));
return faces; // 返回检测到的人脸矩形列表
}
private:
cv::CascadeClassifier faceCascade; // 分类器对象
};
// 使用示例
int main() {
cv::VideoCapture cap(0); // 打开默认摄像头
if (!cap.isOpened()) {
std::cerr << "Error: Could not open camera." << std::endl;
return -1; // 如果打开摄像头失败,则退出程序
}
FaceDetector faceDetector; // 创建人脸检测器
while (true) {
cv::Mat frame;
cap >> frame; // 从摄像头读取图像
if (frame.empty()) {
std::cerr << "Error: Empty frame." << std::endl;
break; // 如果读取到空图像,则跳出循环
}
std::vector<cv::Rect> faces = faceDetector.detectFaces(frame); // 检测人脸
// 绘制检测到的人脸矩形框
for (const auto& face : faces) {
cv::rectangle(frame, face, cv::Scalar(255, 0, 0), 2); // 用蓝色矩形框标出人脸
}
cv::imshow("Face Detection", frame); // 显示检测结果
if (cv::waitKey(30) >= 0) break; // 按任意键退出
}
return 0;
}
4.2.3 逻辑解释
-
加载分类器:
- 在
FaceDetector
类的构造函数中,调用faceCascade.load
方法加载预训练的Haar特征分类器文件haarcascade_frontalface_alt.xml
。如果加载失败,程序将输出错误信息并终止。这一过程是确保人脸检测模块能够正常工作的前提。
- 在
-
人脸检测:
-
frame:输入的图像,类型为
cv::Mat
。 -
faces:输出参数,返回检测到的人脸区域,类型为
std::vector<cv::Rect>
。 -
scaleFactor:每次图像尺寸减小的比例(1.1表示每次缩小10%),有助于检测不同大小的人脸。
-
minNeighbors:检测到的人脸必须至少有多少个邻近矩形(设置为3,以减少错误检测)。
-
flags:一般设置为0,表示使用默认行为。
-
minSize:检测人脸的最小尺寸(30x30像素),用于排除过小的检测区域。
-
detectFaces
方法通过调用faceCascade.detectMultiScale
进行人脸检测。该方法的参数如下:
-
-
绘制人脸矩形:
- 在主函数中,使用循环从摄像头获取图像,并调用
detectFaces
方法进行人脸检测。对于每个检测到的人脸,使用cv::rectangle
在图像上绘制矩形框,框的颜色为蓝色(cv::Scalar(255, 0, 0)
),线条宽度为2像素。最后,使用cv::imshow
显示。
- 在主函数中,使用循环从摄像头获取图像,并调用
4.2.4 时序图
以下是人脸检测模块的时序图,展示了模块之间的交互和数据流:
User Camera OpenCV FaceDetector 请求图像 发送图像 传递图像 返回检测到的人脸 显示图像与人脸框 User Camera OpenCV FaceDetector
4.3 人脸图像预处理模块
4.3.1 模块概述
在检测到人脸后,预处理模块对获取的人脸图像进行处理,以提高后续识别的准确性。常见的预处理操作包括灰度化、直方图均衡化和归一化等。这些操作有助于增强人脸特征,减小光照变化和肤色差异的影响。
4.3.2 代码实现
c
#include <opencv2/opencv.hpp>
class FacePreprocessor {
public:
cv::Mat preprocess(const cv::Mat& face) {
cv::Mat gray, equalized;
// 将人脸图像转换为灰度图
cv::cvtColor(face, gray, cv::COLOR_BGR2GRAY);
// 直方图均衡化
cv::equalizeHist(gray, equalized);
// 归一化到0-1范围
cv::Mat normalized;
equalized.convertTo(normalized, CV_32F, 1.0 / 255.0);
return normalized; // 返回处理后的图像
}
};
// 使用示例
int main() {
cv::VideoCapture cap(0); // 打开默认摄像头
if (!cap.isOpened()) {
std::cerr << "Error: Could not open camera." << std::endl;
return -1; // 如果打开摄像头失败,则退出程序
}
FaceDetector faceDetector; // 创建人脸检测器
FacePreprocessor facePreprocessor; // 创建人脸预处理器
while (true) {
cv::Mat frame;
cap >> frame; // 从摄像头读取图像
if (frame.empty()) {
std::cerr << "Error: Empty frame." << std::endl;
break; // 如果读取到空图像,则跳出循环
}
std::vector<cv::Rect> faces = faceDetector.detectFaces(frame); // 检测人脸
// 处理每个检测到的人脸
for (const auto& face : faces) {
cv::Mat detectedFace = frame(face); // 提取人脸区域
cv::Mat processedFace = facePreprocessor.preprocess(detectedFace); // 预处理人脸图像
// 显示处理后的人脸图像
cv::imshow("Processed Face", processedFace);
}
cv::imshow("Face Detection", frame); // 显示检测结果
if (cv::waitKey(30) >= 0) break; // 按任意键退出
}
return 0;
}
4.3.3 逻辑解释
-
灰度化:
- 在
preprocess
方法中,首先使用cv::cvtColor
将输入的人脸图像转换为灰度图像。灰度化有助于减少数据处理的复杂性,并强调人脸的结构信息。
- 在
-
直方图均衡化:
- 接着,调用
cv::equalizeHist
对灰度图像进行直方图均衡化。该操作可以增强图像的对比度,使得人脸特征更加明显,从而提高后续识别的准确性。
- 接着,调用
-
归一化:
- 最后,将均衡化后的图像转换为浮点型,并归一化到0-1的范围,便于后续的特征提取和分类。这一过程通过
convertTo
函数完成,设置转换到CV_32F
类型并乘以1.0 / 255.0
。
- 最后,将均衡化后的图像转换为浮点型,并归一化到0-1的范围,便于后续的特征提取和分类。这一过程通过
4.3.4 时序图
以下是人脸图像预处理模块的时序图,展示输入和输出的关系:
User FaceDetector Preprocessor 传递人脸图像 处理人脸图像 返回处理后的人脸图像 显示处理后的人脸图像 User FaceDetector Preprocessor
4.4 人脸识别模块(基于LBP特征)
4.4.1 模块概述
该模块负责从预处理后的图像中提取人脸特征并进行识别。在本项目中,我们采用局部二值模式(Local Binary Pattern, LBP)作为特征描述符,并使用简单的分类器(如K近邻算法)进行识别。LBP是一种纹理特征提取方法,通过比较每个像素与其周围像素的灰度值,生成二进制模式。
4.4.2 代码实现
c
#include <opencv2/opencv.hpp>
#include <opencv2/ml.hpp> // 引入机器学习模块
#include <iostream>
class FaceRecognizer {
public:
FaceRecognizer() {
// 初始化K近邻分类器
model = cv::ml::KNearest::create();
}
void train(const std::vector<cv::Mat>& images, const std::vector<int>& labels) {
// 准备训练数据
cv::Mat trainingData;
for (const auto& img : images) {
cv::Mat lbpImage = extractLBP(img); // 提取LBP特征
trainingData.push_back(lbpImage.reshape(1, 1)); // 将图像展平并添加到训练数据
}
model->train(trainingData, cv::ml::ROW_SAMPLE, cv::Mat(labels)); // 训练模型
}
int predict(const cv::Mat& face) {
cv::Mat lbpImage = extractLBP(face); // 提取特征
int response = static_cast<int>(model->predict(lbpImage.reshape(1, 1))); // 预测类别
return response; // 返回识别结果
}
private:
cv::Ptr<cv::ml::KNearest> model; // K近邻分类器
cv::Mat extractLBP(const cv::Mat& image) {
// LBP特征提取实现
cv::Mat lbpImage(image.size(), CV_8UC1, cv::Scalar(0)); // 创建LBP图像
for (int i = 1; i < image.rows - 1; ++i) {
for (int j = 1; j < image.cols - 1; ++j) {
unsigned char center = image.at<uchar>(i, j);
unsigned char code = 0;
// 计算LBP值
code |= (image.at<uchar>(i - 1, j - 1) > center) << 7; // 左上
code |= (image.at<uchar>(i - 1, j) > center) << 6; // 上
code |= (image.at<uchar>(i - 1, j + 1) > center) << 5; // 右上
code |= (image.at<uchar>(i, j + 1) > center) << 4; // 右
code |= (image.at<uchar>(i + 1, j + 1) > center) << 3; // 右下
code |= (image.at<uchar>(i + 1, j) > center) << 2; // 下
code |= (image.at<uchar>(i + 1, j - 1) > center) << 1; // 左下
code |= (image.at<uchar>(i, j - 1) > center) << 0; // 左
lbpImage.at<uchar>(i, j) = code; // 存储LBP值
}
}
return lbpImage; // 返回LBP图像
}
};
// 使用示例
int main() {
cv::VideoCapture cap(0); // 打开默认摄像头
if (!cap.isOpened()) {
std::cerr << "Error: Could not open camera." << std::endl;
return -1; // 如果打开摄像头失败,则退出程序
}
FaceDetector faceDetector; // 创建人脸检测器
FacePreprocessor facePreprocessor; // 创建人脸预处理器
FaceRecognizer faceRecognizer; // 创建人脸识别器
// 假设我们已经准备好训练数据
std::vector<cv::Mat> trainingImages; // 存储训练图像
std::vector<int> labels; // 存储标签
// 假设我们已经准备好训练数据
// 这里可以使用相应的代码加载训练图像和标签
// 例如:
// trainingImages.push_back(cv::imread("path/to/image1.jpg", cv::IMREAD_GRAYSCALE));
// labels.push_back(1); // 标签1代表某个身份
// 继续添加更多训练数据...
// 开始训练模型
faceRecognizer.train(trainingImages, labels); // 训练模型
while (true) {
cv::Mat frame;
cap >> frame; // 从摄像头读取图像
if (frame.empty()) {
std::cerr << "Error: Empty frame." << std::endl;
break; // 如果读取到空图像,则跳出循环
}
std::vector<cv::Rect> faces = faceDetector.detectFaces(frame); // 检测人脸
// 处理每个检测到的人脸
for (const auto& face : faces) {
cv::Mat detectedFace = frame(face); // 提取人脸区域
cv::Mat processedFace = facePreprocessor.preprocess(detectedFace); // 预处理人脸图像
int label = faceRecognizer.predict(processedFace); // 识别人脸
std::string labelText = "Label: " + std::to_string(label);
// 在图像上标注识别结果
cv::putText(frame, labelText, cv::Point(face.x, face.y - 10), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 255, 0), 2);
cv::rectangle(frame, face, cv::Scalar(255, 0, 0), 2); // 用蓝色矩形框标出人脸
}
cv::imshow("Face Recognition", frame); // 显示检测与识别结果
if (cv::waitKey(30) >= 0) break; // 按任意键退出
}
return 0;
}
4.4.3 逻辑解释
-
训练模型:
- 在主函数中,程序假设已经准备好训练数据,包括训练图像和相应的标签。通过调用
faceRecognizer.train(trainingImages, labels)
来训练模型。
- 在主函数中,程序假设已经准备好训练数据,包括训练图像和相应的标签。通过调用
-
实时识别:
-
在循环中,从摄像头获取图像并进行人脸检测。
-
对每个检测到的人脸区域,使用
facePreprocessor.preprocess
方法进行图像预处理,然后调用faceRecognizer.predict(processedFace)
进行人脸识别。 -
识别结果(标签)通过
cv::putText
在图像上标注,并在检测到的人脸周围绘制矩形框以突出显示。
-
-
显示结果:
- 使用
cv::imshow
函数实时显示检测和识别结果,直到用户按下任意键结束程序。
- 使用
4.4.4 时序图
下面是人脸识别模块的时序图,展示了各模块之间的交互和数据流。
User Camera FaceDetector FacePreprocessor FaceRecognizer 请求图像 发送图像 提取人脸区域 返回处理后的人脸图像 显示检测到的人脸 传递处理后的人脸图像 返回识别结果 显示识别结果 User Camera FaceDetector FacePreprocessor FaceRecognizer
五、项目总结
本项目基于嵌入式STM32MP157开发板,成功搭建了一个以Linux操作系统为基础的人脸识别系统,形成了一个合理有效的软硬件开发平台。项目的实现过程包括多个关键模块的设计与开发,具体包括以下几个方面:
5.1 系统架构
系统架构分为宿主机和目标机两部分,宿主机用于开发与调试,目标机则负责实时运行人脸识别功能。宿主机环境中安装了Qt和OpenCV库,并进行了必要的配置,以支持后续的开发工作。同时,目标机运行嵌入式Linux,实现了对人脸图像的采集、检测、预处理和识别。
5.2 功能模块
在项目中实现了四个主要功能模块:
-
图像采集模块:
- 通过USB摄像头实时采集图像,并将图像数据传递给后续处理模块。该模块使用OpenCV库,确保了图像采集的实时性和稳定性。
-
人脸检测模块(基于AdaBoost):
- 利用Haar特征分类器实现对图像中人脸的快速检测。该模块能够有效地识别不同大小和位置的人脸,为后续处理提供了准确的输入。
-
人脸图像预处理模块:
- 对检测到的人脸图像进行灰度化、直方图均衡化和归一化等预处理操作,以提高后续识别的准确性。通过这些处理,减少了光照变化和肤色差异的影响,使得人脸特征更加突出。
-
人脸识别模块(基于LBP特征):
- 使用局部二值模式(LBP)作为特征描述符,并利用K近邻(KNN)分类器进行人脸识别。该模块能够高效、准确地识别出不同身份的人脸,通过实时反馈提高了系统的互动性。