本项目旨在设计并实现一个基于STM32的全栈人脸识别考勤系统。该系统结合了嵌入式开发、计算机视觉和数据库技术,实现了自动人脸检测、识别和考勤记录功能。
主要特点:
主要硬件模块及功能:
以下是使用OpenCV实现人脸检测的代码示例:
#include #include using namespace cv; class FaceDetector { private: CascadeClassifier face_cascade; public: FaceDetector(const std::string& cascade_file) { // 加载Haar级联分类器 if (!face_cascade.load(cascade_file)) { throw std::runtime_error("Error loading face cascade file"); } } std::vector detectFaces(const Mat& frame) { Mat gray; std::vector faces; // 转换为灰度图像 cvtColor(frame, gray, COLOR_BGR2GRAY); // 执行人脸检测 face_cascade.detectMultiScale(gray, faces, 1.1, 3, 0, Size(30, 30)); return faces; } void drawFaces(Mat& frame, const std::vector& faces) { for (const auto& face : faces) { rectangle(frame, face, Scalar(255, 0, 0), 2); } } };
代码说明:
FaceDetector
类封装了人脸检测功能。detectFaces
方法接收一帧图像,返回检测到的人脸矩形区域。drawFaces
方法在原图上绘制检测到的人脸矩形框。
下面是使用LBPH算法实现人脸识别的代码示例:
#include #include using namespace cv; using namespace cv::face; class FaceRecognizer { private: Ptr model; public: FaceRecognizer() { model = LBPHFaceRecognizer::create(); } void train(const std::vector& faces, const std::vector& labels) { model->train(faces, labels); } void predict(const Mat& face, int& label, double& confidence) { model->predict(face, label, confidence); } void saveModel(const std::string& filename) { model->save(filename); } void loadModel(const std::string& filename) { model->read(filename); } };
代码说明:
FaceRecognizer
类封装了LBPH人脸识别器的功能。train
方法用于训练模型。predict
方法进行人脸识别,返回预测的标签和置信度。saveModel
和loadModel
方法用于保存和加载训练好的模型。使用SQLite进行数据库操作的代码示例:
#include #include #include #include class Database { private: sqlite3* db; static int callback(void* data, int argc, char** argv, char** azColName) { // 处理查询结果的回调函数 for(int i = 0; i < argc; i++) { std::cout << azColName[i] << " = " << (argv[i] ? argv[i] : "NULL") << std::endl; } std::cout << std::endl; return 0; } public: Database(const std::string& dbName) { if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) { throw std::runtime_error("Can't open database: " + std::string(sqlite3_errmsg(db))); } } ~Database() { sqlite3_close(db); } void executeQuery(const std::string& sql) { char* errMsg = nullptr; int rc = sqlite3_exec(db, sql.c_str(), callback, 0, &errMsg); if (rc != SQLITE_OK) { std::string error = "SQL error: " + std::string(errMsg); sqlite3_free(errMsg); throw std::runtime_error(error); } } void insertEmployee(const std::string& name, int id) { std::string sql = "INSERT INTO employees (name, id) VALUES ('" + name + "', " + std::to_string(id) + ");"; executeQuery(sql); } void recordAttendance(int employeeId, const std::string& timestamp) { std::string sql = "INSERT INTO attendance (employee_id, timestamp) VALUES (" + std::to_string(employeeId) + ", '" + timestamp + "');"; executeQuery(sql); } };
代码说明:
Database
类封装了SQLite数据库的基本操作。executeQuery
方法执行SQL查询,使用回调函数处理结果。insertEmployee
方法插入新员工记录。recordAttendance
方法记录考勤信息。以下是使用Qt实现主界面的代码示例:
#include #include #include #include class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { setWindowTitle("人脸识别考勤系统"); QWidget *centralWidget = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(centralWidget); QPushButton *btnAttendance = new QPushButton("考勤签到", this); QPushButton *btnManage = new QPushButton("员工管理", this); QPushButton *btnReport = new QPushButton("考勤报表", this); layout->addWidget(btnAttendance); layout->addWidget(btnManage); layout->addWidget(btnReport); setCentralWidget(centralWidget); connect(btnAttendance, &QPushButton::clicked, this, &MainWindow::onAttendanceClicked); connect(btnManage, &QPushButton::clicked, this, &MainWindow::onManageClicked); connect(btnReport, &QPushButton::clicked, this, &MainWindow::onReportClicked); } private slots: void onAttendanceClicked() { // 打开考勤签到界面 QMessageBox::information(this, "考勤签到", "正在打开摄像头进行人脸识别..."); // 这里可以调用人脸识别和考勤记录的相关函数 } void onManageClicked() { // 打开员工管理界面 QMessageBox::information(this, "员工管理", "正在打开员工管理界面..."); // 这里可以实现一个新的对话框或窗口来管理员工信息 } void onReportClicked() { // 生成考勤报表 QMessageBox::information(this, "考勤报表", "正在生成考勤报表..."); // 这里可以实现报表生成和显示的功能 } }; // 主函数 int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow mainWindow; mainWindow.show(); return app.exec(); }
代码说明:
MainWindow
类继承自QMainWindow
,实现了主界面的布局和功能。QVBoxLayout
垂直布局来排列按钮。connect
函数将按钮的点击事件与相应的槽函数连接。onAttendanceClicked
、onManageClicked
和onReportClicked
分别处理不同按钮的点击事件。以下是STM32与Qt程序通过串口通信的示例代码:
// STM32端代码(使用HAL库) #include "stm32f4xx_hal.h" UART_HandleTypeDef huart2; void UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; HAL_UART_Init(&huart2); } void SendData(uint8_t* data, uint16_t size) { HAL_UART_Transmit(&huart2, data, size, HAL_MAX_DELAY); } // Qt端代码 #include #include class SerialCommunication : public QObject { Q_OBJECT public: SerialCommunication(QObject *parent = nullptr) : QObject(parent) { serial = new QSerialPort(this); connect(serial, &QSerialPort::readyRead, this, &SerialCommunication::handleReadyRead); } bool openPort(const QString &portName) { serial->setPortName(portName); serial->setBaudRate(QSerialPort::Baud115200); return serial->open(QIODevice::ReadWrite); } void closePort() { if (serial->isOpen()) { serial->close(); } } void sendData(const QByteArray &data) { if (serial->isOpen()) { serial->write(data); } } private slots: void handleReadyRead() { QByteArray data = serial->readAll(); emit dataReceived(data); } signals: void dataReceived(const QByteArray &data); private: QSerialPort *serial; }; // 在主窗口中使用SerialCommunication类 class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { serialComm = new SerialCommunication(this); connect(serialComm, &SerialCommunication::dataReceived, this, &MainWindow::onDataReceived); // 初始化串口 if (serialComm->openPort("COM3")) { // 根据实际情况修改串口名 qDebug() << "Serial port opened successfully"; } else { qDebug() << "Failed to open serial port"; } } private slots: void onDataReceived(const QByteArray &data) { // 处理接收到的数据 qDebug() << "Received data:" << data; // 这里可以添加对接收数据的处理逻辑 } void sendCommandToSTM32(const QString &command) { serialComm->sendData(command.toUtf8()); } private: SerialCommunication *serialComm; };
代码说明:
SerialCommunication
类封装了Qt串口通信的功能。openPort
方法用于打开指定的串口。closePort
方法用于关闭串口。sendData
方法用于发送数据到STM32。handleReadyRead
槽函数处理接收到的数据,并通过信号dataReceived
发送出去。MainWindow
类中,我们创建了SerialCommunication
实例,并连接了数据接收的信号和槽。onDataReceived
槽函数用于处理从STM32接收到的数据。sendCommandToSTM32
方法用于向STM32发送命令。以下是将人脸识别与考勤逻辑集成到Qt应用程序中的示例代码:
#include #include #include #include #include class AttendanceSystem : public QObject { Q_OBJECT public: AttendanceSystem(QObject *parent = nullptr) : QObject(parent) { faceDetector = new FaceDetector("haarcascade_frontalface_default.xml"); faceRecognizer = new FaceRecognizer(); database = new Database("attendance.db"); camera = new QCamera(this); imageCapture = new QCameraImageCapture(camera); connect(imageCapture, &QCameraImageCapture::imageCaptured, this, &AttendanceSystem::processCapturedImage); // 每5秒捕获一次图像 QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &AttendanceSystem::captureImage); timer->start(5000); } public slots: void startAttendance() { camera->start(); } void stopAttendance() { camera->stop(); } private slots: void captureImage() { imageCapture->capture(); } void processCapturedImage(int id, const QImage &preview) { cv::Mat frame = QImageToMat(preview); std::vector faces = faceDetector->detectFaces(frame); for (const auto& face : faces) { cv::Mat faceROI = frame(face); int label; double confidence; faceRecognizer->predict(faceROI, label, confidence); if (confidence < 80.0) { // 假设置信度阈值为80 recordAttendance(label); emit attendanceRecorded(label); } } } void recordAttendance(int employeeId) { QDateTime currentTime = QDateTime::currentDateTime(); QString timestamp = currentTime.toString("yyyy-MM-dd hh:mm:ss"); database->recordAttendance(employeeId, timestamp.toStdString()); } private: FaceDetector *faceDetector; FaceRecognizer *faceRecognizer; Database *database; QCamera *camera; QCameraImageCapture *imageCapture; cv::Mat QImageToMat(const QImage &image) { cv::Mat mat; switch (image.format()) { case QImage::Format_RGB888: mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR); break; case QImage::Format_ARGB32_Premultiplied: mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); cv::cvtColor(mat, mat, cv::COLOR_RGBA2BGR); break; default: break; } return mat; } signals: void attendanceRecorded(int employeeId); };
代码说明:
AttendanceSystem
类集成了整个考勤系统的核心功能,包括摄像头控制、图像处理、人脸识别和考勤记录。
构造函数中:
FaceDetector
、FaceRecognizer
和 Database
对象。startAttendance()
和 stopAttendance()
方法用于启动和停止考勤过程。
captureImage()
槽函数被定时器触发,用于捕获摄像头图像。
processCapturedImage()
是核心处理函数:
recordAttendance()
方法将考勤记录保存到数据库中,包括员工ID和时间戳。
QImageToMat()
是一个辅助函数,用于将 Qt 的 QImage 转换为 OpenCV 的 Mat 格式。
类中定义了 attendanceRecorded
信号,当成功记录考勤时发出,可用于更新UI或通知其他组件。
整个系统通过定时捕获图像、检测人脸、识别身份、记录考勤的流程,实现了自动化的考勤功能。
该设计允许系统在后台持续运行,不需要人工干预即可完成考勤过程。
通过调整人脸识别的置信度阈值(此处设为80.0),可以平衡系统的准确性和灵敏度。
系统集成了数据库操作,确保考勤记录能够被永久保存和后续查询。