STM32全栈嵌入式人脸识别考勤系统:融合OpenCV、Qt和SQLite的解决方案

1. 项目概述

本项目旨在设计并实现一个基于STM32的全栈人脸识别考勤系统。该系统结合了嵌入式开发、计算机视觉和数据库技术,实现了自动人脸检测、识别和考勤记录功能。

主要特点:

  • 使用STM32F4系列微控制器作为主控制器
  • 采用OpenCV进行人脸检测和识别
  • Qt开发跨平台桌面应用程序,实现友好的用户界面
  • SQLite嵌入式数据库存储员工信息和考勤记录
  • 支持实时考勤、数据统计分析和报表生成

2. 系统设计

2.1 硬件设计

主要硬件模块及功能:

  • STM32F407VGT6微控制器:系统的核心,负责协调各个模块工作
  • OV7670摄像头模块:捕获实时图像,用于人脸检测和识别
  • 3.5寸TFT LCD显示屏:显示系统界面和识别结果
  • AS608指纹识别模块:作为辅助识别手段
  • RC522 RFID读卡器:用于员工卡识别,提供备用签到方式
  • ESP8266 WiFi模块:实现与服务器的无线通信,上传考勤数据

2.2 软件设计

3. 代码实现

3.1 人脸检测

以下是使用OpenCV实现人脸检测的代码示例:

#include <opencv2/opencv.hpp>
#include <opencv2/objdetect.hpp>

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<Rect> detectFaces(const Mat& frame) {
        Mat gray;
        std::vector<Rect> 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<Rect>& faces) {
        for (const auto& face : faces) {
            rectangle(frame, face, Scalar(255, 0, 0), 2);
        }
    }
};

代码说明:

  1. FaceDetector类封装了人脸检测功能。
  2. 构造函数加载Haar级联分类器文件。
  3. detectFaces方法接收一帧图像,返回检测到的人脸矩形区域。
  4. drawFaces方法在原图上绘制检测到的人脸矩形框。

 

3.2 人脸识别

下面是使用LBPH算法实现人脸识别的代码示例:

#include <opencv2/face.hpp>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace cv::face;

class FaceRecognizer {
private:
    Ptr<LBPHFaceRecognizer> model;

public:
    FaceRecognizer() {
        model = LBPHFaceRecognizer::create();
    }

    void train(const std::vector<Mat>& faces, const std::vector<int>& 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);
    }
};

代码说明:

  1. FaceRecognizer类封装了LBPH人脸识别器的功能。
  2. 构造函数创建LBPH人脸识别器实例。
  3. train方法用于训练模型。
  4. predict方法进行人脸识别,返回预测的标签和置信度。
  5. saveModelloadModel方法用于保存和加载训练好的模型。

3.3 数据库操作

使用SQLite进行数据库操作的代码示例:

#include <sqlite3.h>
#include <string>
#include <stdexcept>
#include <iostream>

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);
    }
};

代码说明:

  1. Database类封装了SQLite数据库的基本操作。
  2. 构造函数打开数据库连接,析构函数关闭连接。
  3. executeQuery方法执行SQL查询,使用回调函数处理结果。
  4. insertEmployee方法插入新员工记录。
  5. recordAttendance方法记录考勤信息。

3.4 Qt界面实现

以下是使用Qt实现主界面的代码示例:

#include <QMainWindow>
#include <QPushButton>
#include <QVBoxLayout>
#include <QMessageBox>

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();
}

代码说明:

  1. MainWindow类继承自QMainWindow,实现了主界面的布局和功能。
  2. 构造函数中创建了三个按钮:考勤签到、员工管理和考勤报表。
  3. 使用QVBoxLayout垂直布局来排列按钮。
  4. 通过connect函数将按钮的点击事件与相应的槽函数连接。
  5. 三个槽函数onAttendanceClickedonManageClickedonReportClicked分别处理不同按钮的点击事件。
  6. 主函数创建并显示主窗口,启动Qt事件循环。

3.5 STM32与Qt通信

以下是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 <QSerialPort>
#include <QSerialPortInfo>

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;
};

代码说明:

  1. SerialCommunication类封装了Qt串口通信的功能。
  2. openPort方法用于打开指定的串口。
  3. closePort方法用于关闭串口。
  4. sendData方法用于发送数据到STM32。
  5. handleReadyRead槽函数处理接收到的数据,并通过信号dataReceived发送出去。
  6. MainWindow类中,我们创建了SerialCommunication实例,并连接了数据接收的信号和槽。
  7. onDataReceived槽函数用于处理从STM32接收到的数据。
  8. sendCommandToSTM32方法用于向STM32发送命令。

3.6 人脸识别与考勤逻辑集成

以下是将人脸识别与考勤逻辑集成到Qt应用程序中的示例代码:

#include <QCamera>
#include <QCameraImageCapture>
#include <QTimer>
#include <QDateTime>
#include <opencv2/opencv.hpp>

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<cv::Rect> 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);
};

代码说明:

  1. AttendanceSystem 类集成了整个考勤系统的核心功能,包括摄像头控制、图像处理、人脸识别和考勤记录。

  2. 构造函数中:

    • 初始化 FaceDetectorFaceRecognizer 和 Database 对象。
    • 设置 QCamera 和 QCameraImageCapture 用于图像捕获。
    • 创建一个定时器,每5秒触发一次图像捕获。
  3. startAttendance() 和 stopAttendance() 方法用于启动和停止考勤过程。

  4. captureImage() 槽函数被定时器触发,用于捕获摄像头图像。

  5. processCapturedImage() 是核心处理函数:

    • 将 QImage 转换为 OpenCV 的 Mat 格式。
    • 使用 FaceDetector 检测人脸。
    • 对每个检测到的人脸进行识别。
    • 如果识别置信度高于阈值,则记录考勤。
  6. recordAttendance() 方法将考勤记录保存到数据库中,包括员工ID和时间戳。

  7. QImageToMat() 是一个辅助函数,用于将 Qt 的 QImage 转换为 OpenCV 的 Mat 格式。

  8. 类中定义了 attendanceRecorded 信号,当成功记录考勤时发出,可用于更新UI或通知其他组件。

  9. 整个系统通过定时捕获图像、检测人脸、识别身份、记录考勤的流程,实现了自动化的考勤功能。

  10. 该设计允许系统在后台持续运行,不需要人工干预即可完成考勤过程。

  11. 通过调整人脸识别的置信度阈值(此处设为80.0),可以平衡系统的准确性和灵敏度。

  12. 系统集成了数据库操作,确保考勤记录能够被永久保存和后续查询。

4. 项目总结

4.1 主要成果

  1. 成功实现了基于STM32和Qt的全栈人脸识别考勤系统。
  2. 集成了实时人脸检测和识别功能,提高了考勤效率。
  3. 开发了友好的用户界面,方便管理员操作和数据查询。
  4. 实现了考勤数据的自动化记录和统计分析功能。

4.2 技术亮点

  1. 采用OpenCV进行图像处理和人脸识别,提高了识别的准确性。
  2. 使用Qt框架开发跨平台桌面应用,提升了用户体验。
  3. 集成SQLite数据库,实现了高效的数据管理和查询。
  4. STM32与Qt的串口通信实现,使硬件控制更加灵活。

 

 

  • 44
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

极客小张

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值