欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于OpenCV實現(xiàn)的人臉簽到系統(tǒng)源代碼

 更新時間:2024年04月27日 09:56:43   作者:狗蛋兒l  
本文從實際背景和需求出發(fā),采用人臉識別簽到考勤改變了傳統(tǒng)人工檢驗的做法,極大提高了組織效率和辦事能力,這篇文章主要給大家介紹了關于如何基于OpenCV實現(xiàn)的人臉簽到系統(tǒng)的相關資料,需要的朋友可以參考下

效果圖

目錄文件

camerathread.h 功能實現(xiàn)全寫在.h里了

class CameraThread : public QThread
{
    Q_OBJECT
public:
    CameraThread()
    {
        //打開序號為0的攝像頭
        m_cap.open(0);
        if (!m_cap.isOpened()) {
            qDebug() << "Error: Cannot open camera";
        }

        //判斷是否有文件,人臉識別模型,檢測和識別用的
        if(!m_cascada.load("D:/research/CV/Opencv/haarcascade_frontalface_alt2.xml"))
        {
            QMessageBox::information(NULL,"失敗", "人臉識別模型裝載失敗");
        }
        //實例化定時器,子線程中數(shù)據(jù)傳輸?shù)街芯€程并顯示出來
        m_timer = new QTimer(this);
        //綁定時間信號及獲取圖像幀的圖像
        connect(m_timer,SIGNAL(timeout()),this,SLOT(readFarme()));
        //大概每秒24幀
        //開始定時器
        m_timer->start(42);

        //打卡模塊
        // 定義 FisherFaceRecognizer 模型,訓練用的模型
        m_model = LBPHFaceRecognizer::create();
        // 加載訓練好的模型,自己訓練的模型
        m_model->read("MyFaceLBPHModel.xml");

        if (m_model.empty())
        {
            qDebug() << "Error: Failed to load model!";
        }
        else
        {
            qDebug() << "Model loaded successfully!";
        }

        //錄入時候的定時器,錄入大概幾秒,獲取二十張灰色圖像并保存,等待訓練
        //實例化定時器
        m_Very_timer = new QTimer(this);

        //數(shù)據(jù)庫的初始化部分
        //鏈接數(shù)據(jù)庫
        m_db = QSqlDatabase::addDatabase("QMYSQL");
        m_db.setHostName("localhost");  // 主機名
        m_db.setDatabaseName("face");  // 數(shù)據(jù)庫名
        m_db.setUserName("root");  // 用戶名
        m_db.setPassword("31415926");  // 密碼

    }
    ~CameraThread()
    {
        //釋放攝像頭
        m_cap.release();
    }
    void run() override
    {


    }


    //圖像數(shù)據(jù)類型轉換
    QImage MatImageToQt(const cv::Mat &src)
    {
        if(src.type() == CV_8UC1)
        {
            QImage qImage(src.cols,src.rows,QImage::Format_Indexed8);
            qImage.setColorCount(256);
            for(int i = 0; i < 256; i ++)
            {
                qImage.setColor(i,qRgb(i,i,i));
            }
            uchar *pSrc = src.data;
            for(int row = 0; row < src.rows; row ++)
            {
                uchar *pDest = qImage.scanLine(row);
                memcmp(pDest,pSrc,src.cols);
                pSrc += src.step;
            }
            return qImage;
        }
        else if(src.type() == CV_8UC3)
        {
            const uchar *pSrc = (const uchar*)src.data;
            QImage qImage(pSrc,src.cols,src.rows,src.step,QImage::Format_RGB888);
            return qImage.rgbSwapped();
        }
        else if(src.type() == CV_8UC4)
        {
            const uchar *pSrc = (const uchar*)src.data;
            QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32);
            return qImage.copy();
        }
        else
        {
            return QImage();
        }
    }

    //人臉檢測
    void Check(Mat &image, Mat &gray)
    {
        //直方圖均勻化(改善圖像的對比度和亮度)
        Mat equalizedImg;
        equalizeHist(gray,equalizedImg);
        int flags = CASCADE_SCALE_IMAGE; //檢測多個人

        Size minFeatureSize(30,30);
        float searchScaleFactor = 1.1f; //默認1.1倍
        int minNeighbors = 4;

        m_cascada.detectMultiScale(equalizedImg,m_faces,searchScaleFactor,minNeighbors,flags,minFeatureSize);
        m_current_people = m_faces.size();
        //檢測到的個數(shù)
        //qDebug() << "檢測到人臉的個數(shù):" << m_faces.size() << endl;
        QString str;
        str.setNum(m_faces.size());
        //qDebug() << m_current_people << endl;

//        //畫矩形框
        Mat face;
//        for(int i = 0; i < m_faces.size(); i++)
//        {
//            if(m_faces[i].height > 0 && m_faces[i].width >0 )
//            {
//                face = gray(m_faces[i]);
//                m_text_lb = Point(m_faces[i].x,m_faces[i].y);
//                rectangle(image, m_faces[i], Scalar(50, 50, 150), 2, 8, 0); //線太細了會導致在QLabel上面丟失線框


//            }
//            int iP = Predict(image);

//        }



        // 畫矩形框和顯示姓名
        for(int i = 0; i < m_faces.size(); i++)
        {
            string name; // 從數(shù)據(jù)庫中獲取姓名
            if(m_faces[i].height > 0 && m_faces[i].width >0 )
            {
                face = gray(m_faces[i]);
                m_text_lb = Point(m_faces[i].x,m_faces[i].y);
                rectangle(image, m_faces[i], Scalar(50, 50, 150), 2, 8, 0); // 畫矩形框

                //灰度圖
                Mat tImagGray;
                cvtColor(image, tImagGray, COLOR_BGR2GRAY);
                // 識別人臉
                int id = Predict(tImagGray);


                if (!m_db.open())
                {
                    qDebug() << "Failed to connect to database:" ;
                }

                // 執(zhí)行查詢語句
                QSqlQuery query;
                QString queryString = QString("SELECT name FROM staff_info WHERE num = %1").arg(id);
                if (!query.exec(queryString))
                {
                    qDebug() << "Failed to execute query:";
                    m_db.close();
                }

                // 處理查詢結果
                if (query.next())
                {
                    name = query.value(0).toString().toUtf8().constData();
                    m_db.close();
                }
                else
                {
                    m_db.close();
                    name = "Unknown";
                }
            }
                qDebug() << name.data();
                // 在圖像上顯示姓名
                Point textPosition(m_faces[i].x, m_faces[i].y - 10); // 文本位置在矩形框上方一點
                putText(image, name.data(), textPosition, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 255, 255), 1, LINE_AA);


        }

        m_faces.clear();
    }



    // 裁剪出的臉部區(qū)域的圖像
    cv::Mat cropFace(const cv::Mat& faceImg, const cv::Rect& faceRect) {
        // 不再進行顏色空間轉換,直接使用輸入的 faceImg
        cv::Mat frame_gray = faceImg;

        // 確保矩形區(qū)域在圖像范圍內(nèi)
        if (faceRect.x >= 0 && faceRect.y >= 0 &&
            faceRect.x + faceRect.width <= frame_gray.cols &&
            faceRect.y + faceRect.height <= frame_gray.rows)
        {
            // 使用區(qū)域選擇功能提取矩形區(qū)域
            cv::Mat faceROI = frame_gray(faceRect).clone(); // 使用 clone() 來復制圖像區(qū)域
            return faceROI;
        }
        else
        {
            // 處理矩形區(qū)域超出圖像范圍的情況
            // 這里可以選擇合適的處理方式,比如調(diào)整矩形區(qū)域的大小或者放棄處理該臉部區(qū)域
            return cv::Mat();
        }
    }



    // 在文本文件末尾添加數(shù)據(jù)
    void appendToTextFile(const QString& filename, const QString& data)
    {
        QFile file(filename);
        if (file.open(QIODevice::Append | QIODevice::Text))
        {
            QTextStream out(&file);
            out << data << "\n";
            file.close();
        }
        else
        {
            qDebug() << "Failed to open file for appending.";
        }
    }

    //預測功能,檢測輸入的圖像中的人臉是否在訓練集中
    int Predict(Mat src_image)
    {
        Mat face_test;
        int ispredict = 0;
        //截取的ROI人臉尺寸調(diào)整
        if (src_image.rows >= 120)
        {
            //改變圖像大小,使用雙線性差值
            resize(src_image, face_test, Size(92, 112));

        }
        //判斷是否正確檢測ROI
        if (!face_test.empty())
        {
            //測試圖像應該是灰度圖
            ispredict = m_model->predict(face_test);
        }
        //cout << ispredict << endl;
        return ispredict;
    }


signals:
    //向主線程傳輸圖像
    void Set_image(QImage *image);
    //打卡之后設置ui部分和數(shù)據(jù)庫部分
    void Send_Card_Data(QSqlDatabase *m_db,QString num);

public slots:
    //攝像頭讀取函數(shù)
    void readFarme()
    {
        //讀取一幀圖像
        m_cap.read(m_src_image) ;
        //處理一下數(shù)據(jù),人臉檢測
        //生成灰度圖
        Mat dst_gray;
        cvtColor(m_src_image, dst_gray, COLOR_BGR2GRAY);
        Check(m_src_image, dst_gray);

        //轉換圖像數(shù)據(jù)類型
        QImage imag = MatImageToQt(m_src_image);

        //發(fā)送圖像
        Set_image(&imag);
    }

    //執(zhí)行二十次檢測人臉并保存下來的功能
    void Verity()
    {
        //判斷目前攝像頭中的臉有幾個
        if (!m_facenum.isEmpty())
        {
            //如果目前只有一個人
            if(m_current_people == 1)
            {
                qDebug() << m_executionCount;
                //裁剪出人臉區(qū)域
                Mat faceROI = cropFace(m_src_image, m_faces[0]);
                if (!faceROI.empty())
                {
                    // 調(diào)整裁剪后的臉部圖像大小
                    cv::Mat resizedFace;
                    cv::resize(faceROI, resizedFace, cv::Size(92, 112));

                    // 將調(diào)整大小后的臉部圖像存儲在 m_src_image 中
                    m_src_image = resizedFace.clone();
                } else
                {
                    qDebug() << "裁剪后的臉部圖像為空! ";
                }

                //存儲的地址
                QString dir_str = "D:\\research\\CV\\Opencv\\facedata\\" + m_facenum + "\\" + QString::number(m_executionCount) + ".jpg";
                //用來判斷這個地址的文件夾是否存在
                QString is_dir = "D:\\research\\CV\\Opencv\\facedata\\" + m_facenum;
                qDebug() << dir_str;
                QDir dir(is_dir);
                if (!dir.exists())
                {
                    if (!dir.mkpath("."))
                    {
                        qDebug() << "default ";
                    }
                }

                //扣的臉部的圖像如果不為空
                if (!faceROI.empty())
                {
                    string filename = dir_str.toStdString();
                    // 將彩色圖像轉換為灰度圖像
                    cv::Mat frame_gray;
                    cv::cvtColor(faceROI, frame_gray, cv::COLOR_BGR2GRAY);
                    //存儲圖像
                    imwrite(filename, frame_gray);
                    //在存儲訓練集需要的圖片的地址的txt里也更新地址
                    QString csvsave = "D:/research/CV/Opencv/facedata/" + m_facenum + "/" + QString::number(m_executionCount) + ".jpg" + ";" + m_facenum ;
                    //存放要訓練的模型的圖片的地址的txt
                    QString csvfilename = "D:\\research\\CV\\Opencv\\at.txt";
                    //添加數(shù)據(jù)
                    appendToTextFile(csvfilename, csvsave);
                    m_executionCount++; // 每次執(zhí)行計數(shù)器加一

                } else
                {
                    qDebug() << "臉部圖像為空,無法寫入文件! ";
                }

                //存儲20張之后
                if (m_executionCount >= 20)
                {
                    m_executionCount = 0;
                    m_Very_timer->stop();
                }

            }

        }
        else
        {
            QMessageBox::about(NULL, "提示", "請輸入工號!");
            m_executionCount = 0;
            m_Very_timer->stop();
            //delete m_Very_timer;
        }
    }

    //錄入臉
    void Set_Verity_face(QString facenum,QString facename)
    {
        m_facenum = facenum;
        m_facename = facename;
        qDebug()<< QSqlDatabase::drivers();
        //判斷數(shù)據(jù)庫是否開著
        if (!m_db.open())
        {
            qDebug() << "Failed to connect to database:" ;
        } else
        {
            qDebug() << "Connected to database!";
        }

        //查找全部,看數(shù)量以便添加id
        QSqlQuery countQuery("SELECT COUNT(*) FROM staff_info");

        if (countQuery.exec() && countQuery.next())
        {
            int rowCount = countQuery.value(0).toInt(); // 獲取數(shù)據(jù)條數(shù)
            int newId = rowCount + 1; // 新的 ID 就是數(shù)據(jù)條數(shù)加一

            QSqlQuery query;
            query.prepare("INSERT INTO staff_info (num, id, name) VALUES (:facenum, :faceid, :facename)");
            query.bindValue(":facenum", m_facenum);
            query.bindValue(":faceid", newId); // 使用新的 ID
            query.bindValue(":facename", m_facename);

            if (query.exec())
            {
                qDebug() << "Data inserted into database successfully!";
            }
            else
            {
                qDebug() << "Failed to insert data into database:" ;
            }
        }
        else
        {
            qDebug() << "Failed to retrieve row count from database:" ;
        }


        //關閉數(shù)據(jù)庫
        m_db.close();

        qDebug() << "開始錄入\n";

        //綁定時間信號及獲取圖像幀的圖像
        connect(m_Very_timer, SIGNAL(timeout()), this, SLOT(Verity()));
        //開始定時器
        m_Very_timer->start(200);
    }

    //設置打卡部分
    void Set_Card()
    {
        Mat gray;
        cvtColor(m_src_image, gray, CV_BGR2GRAY); // 將輸入圖像轉換為灰度圖像
        vector<Rect> faces; // 存放檢測到的人臉矩形的向量容器

        // 使用級聯(lián)分類器檢測人臉
        m_cascada.detectMultiScale(gray, faces, 1.1, 4, 0, Size(30, 30), Size(500, 500));

        // 遍歷檢測到的每張人臉
        for (size_t i = 0; i < faces.size(); i++)
        {
            Rect faceRect = faces[i]; // 獲取當前人臉的矩形框
            // 從灰度圖像中提取當前人臉區(qū)域
            Mat faceROI = gray(faceRect);

            // 檢查人臉區(qū)域是否為空
            if (!faceROI.empty())
            {
                // 調(diào)用 Predict 函數(shù)對人臉進行預測
                int temp = Predict(faceROI);
                qDebug() << "預測結果:" << temp;

                // 發(fā)送打卡數(shù)據(jù),然后讓主線程在數(shù)據(jù)庫中查找
                Send_Card_Data(&m_db,QString::number(temp));

            }
        }
        faces.clear();


    }


private:
    //聲明opencv的視頻類
    cv::VideoCapture m_cap;
    //更新顯示的定時器
    QTimer *m_timer;
    //錄入的定時器
    QTimer *m_Very_timer;
    //聲明Mat類圖像變量,存儲當前攝像頭前的圖像
    cv::Mat m_src_image;
    //檢測的分類器
    CascadeClassifier m_cascada;

    //矩形框的點
    Point m_text_lb;
    //人臉個數(shù)
    vector<Rect> m_faces;
    // 聲明一個錄入計數(shù)器變量
    int m_executionCount = 0;
    //工號和姓名
    QString m_facenum;
    QString m_facename;
    //識別的模型
    Ptr<LBPHFaceRecognizer> m_model;
    //當前鏡頭前識別出的人數(shù)
    int m_current_people = 0;
    //數(shù)據(jù)庫
    QSqlDatabase m_db;
};

#endif // CAMERATHREAD_H

faceverify.cpp

#include "faceverify.h"
#include "ui_faceverify.h"

#include <QDebug>
#include <QCamera>              //管理攝像頭的大類
#include <QCameraInfo>          //管理攝像頭的設備表
#include <QCameraViewfinder>    //管理攝像頭顯示區(qū)域
#include <QCameraImageCapture>  //管理圖片
#include <QDateTime>            //管理時間
#include <QString>              //管理字符串
#include "verityface.h"

#pragma execution_character_set("utf-8")

FaceVerify::FaceVerify(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::FaceVerify)
{
    ui->setupUi(this);


}

FaceVerify::~FaceVerify()
{
    delete m_cthread;
    delete ui;
}

void FaceVerify::Get_image(QImage *image)
{
    //設置圖片大小和label的長寬一致
    QImage timage = image->scaled(ui->Camera->width(), ui->Camera->height(),Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    ui->Camera->setPixmap(QPixmap::fromImage(timage));
}

//打開攝像頭
void FaceVerify::on_pushButton_Open_Camera_clicked()
{
    if(ui->pushButton_Open_Camera->text() ==  "打開攝像頭")
    {
        m_cthread = new CameraThread();
        connect(m_cthread, &CameraThread::Set_image, this, &FaceVerify::Get_image);
        ui->pushButton_Open_Camera->setText("關閉攝像頭");
    }
    else
    {

        disconnect(m_cthread, &CameraThread::Set_image, this, &FaceVerify::Get_image);
        delete m_cthread;
        m_cthread = nullptr;
        ui->Camera->clear();
        ui->pushButton_Open_Camera->setText("打開攝像頭");

    }
}

//打卡
void FaceVerify::on_pushButton_Card_clicked()
{
    // 先斷開之前的連接
    disconnect(this, &FaceVerify::Send_Card, m_cthread, &CameraThread::Set_Card);
    disconnect(m_cthread, &CameraThread::Send_Card_Data, this, &FaceVerify::Show_Data);

    // 連接信號和槽
    connect(this, &FaceVerify::Send_Card, m_cthread, &CameraThread::Set_Card);
    connect(m_cthread, &CameraThread::Send_Card_Data, this, &FaceVerify::Show_Data);

    // 發(fā)送信號
    emit Send_Card();

}
//打卡數(shù)據(jù)顯示
void FaceVerify::Show_Data(QSqlDatabase *m_db,QString num)
{
    if (!m_db->open())
    {
        qDebug() << "Failed to connect to database:";
    }
    else
    {
        qDebug() << "Connected to database!";

        // 執(zhí)行檢查并插入數(shù)據(jù)
        QSqlQuery query;
        query.prepare("SELECT * FROM record_info WHERE num = :facenum AND DATE(mtime) = CURDATE()");
        query.bindValue(":facenum", num);

        if (query.exec())
        {
            if (query.next())
            {
                // 如果有記錄,表示今天已經(jīng)簽到
                QMessageBox::information(NULL, tr("提示 "), tr("今天已簽到 "));

                // 讀取數(shù)據(jù)庫中的數(shù)據(jù)并顯示在textEdit_data中
                QString data;
                QSqlRecord record = query.record();
                int numField = record.indexOf("num"); // 獲取字段索引
                int timeField = record.indexOf("mtime");

                // 遍歷查詢結果
                do
                {
                    QString num = query.value(numField).toString(); // 獲取編號
                    QString time = query.value(timeField).toDateTime().toString("yyyy-MM-dd hh:mm:ss"); // 獲取時間
                    data += "編號: " + num + ", 時間: " + time + "\n";
                } while (query.next());

                // 將數(shù)據(jù)顯示在textEdit_data中
                ui->textEdit_data->setText(data);

            }
            else
            {
                QDateTime currentDateTime = QDateTime::currentDateTime();
                QString currentTimeString = currentDateTime.toString("yyyy-MM-dd hh:mm:ss");

                qDebug() << "Current time:" << currentTimeString;
                // 如果沒有記錄,表示今天還未簽到,執(zhí)行插入操作
                query.prepare("INSERT INTO record_info (num, id, mtime) VALUES (:facenum, :faceid, :facemtime)");
                query.bindValue(":facenum", num);
                query.bindValue(":faceid", num); // 使用相同的 num 作為 id
                query.bindValue(":facemtime", currentTimeString);

                if (query.exec())
                {
                    QMessageBox::information(NULL, tr("提示 "), tr("簽到成功 "));
                }
                else
                {
                    QMessageBox::critical(NULL, tr("錯誤 "), tr("簽到失敗: ") ); // 顯示錯誤信息
                }
            }
        }
        else
        {
            QMessageBox::critical(NULL, tr("錯誤"), tr("查詢數(shù)據(jù)失?。?"));
        }

        // 關閉數(shù)據(jù)庫連接
        m_db->close();
    }


}

//錄入人臉
void FaceVerify::on_pushButton_face_clicked()
{
    //打開錄入界面
    m_verifity = new VerityFace();
    connect(m_verifity, &VerityFace::Send_Face, this, &FaceVerify::GetFaceNum);
    m_verifity->show();
}

//訓練模型
void FaceVerify::on_pushButton_Train_clicked()
{

    m_train = new train();
    connect(m_train, &train::finished, this, &FaceVerify::Train_Finish);
    m_train->run();

}
//模型訓練完
void FaceVerify::Train_Finish()
{
    delete m_train;
    QMessageBox::information(NULL,"訓練", "訓練完成");
}

總結 

到此這篇關于基于OpenCV實現(xiàn)的人臉簽到系統(tǒng)的文章就介紹到這了,更多相關OpenCV人臉簽到系統(tǒng)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論