diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/device/face/CasicFaceRecState.cpp b/device/face/CasicFaceRecState.cpp index 3ba8470..7415a86 100644 --- a/device/face/CasicFaceRecState.cpp +++ b/device/face/CasicFaceRecState.cpp @@ -2,5 +2,56 @@ CasicFaceRecState::CasicFaceRecState() { + this->recoginzeId = "0"; + this->timeStamp = -1; + this->state = FaceRecStateName::REC_NOT_START; +} + +void CasicFaceRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noFaceCount = 0; + + LOG(DEBUG) << QString("[CasicFaceRecState][initRecognize] 人脸识别状态初始化").toStdString(); + + // 开始识别 + this->state = FaceRecStateName::REC_DETECT; +} + +QString CasicFaceRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + +QJsonObject CasicFaceRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromLocal8Bit(recoginzeId.data())); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noFaceCount", noFaceCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject faceInfoObj; + faceInfoObj.insert("hasFace", faceInfo->hasFace); + faceInfoObj.insert("reality", faceInfo->antiReality); + faceInfoObj.insert("clarity", faceInfo->antiClarity); + faceInfoObj.insert("sim", faceInfo->sim); + QJsonArray rectArray; + rectArray.append(faceInfo->face.pos.x); + rectArray.append(faceInfo->face.pos.y); + rectArray.append(faceInfo->face.pos.width); + rectArray.append(faceInfo->face.pos.height); + faceInfoObj.insert("faceRect", rectArray); + + obj.insert("faceInfo", faceInfoObj); + + return obj; } diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/device/face/CasicFaceRecState.cpp b/device/face/CasicFaceRecState.cpp index 3ba8470..7415a86 100644 --- a/device/face/CasicFaceRecState.cpp +++ b/device/face/CasicFaceRecState.cpp @@ -2,5 +2,56 @@ CasicFaceRecState::CasicFaceRecState() { + this->recoginzeId = "0"; + this->timeStamp = -1; + this->state = FaceRecStateName::REC_NOT_START; +} + +void CasicFaceRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noFaceCount = 0; + + LOG(DEBUG) << QString("[CasicFaceRecState][initRecognize] 人脸识别状态初始化").toStdString(); + + // 开始识别 + this->state = FaceRecStateName::REC_DETECT; +} + +QString CasicFaceRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + +QJsonObject CasicFaceRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromLocal8Bit(recoginzeId.data())); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noFaceCount", noFaceCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject faceInfoObj; + faceInfoObj.insert("hasFace", faceInfo->hasFace); + faceInfoObj.insert("reality", faceInfo->antiReality); + faceInfoObj.insert("clarity", faceInfo->antiClarity); + faceInfoObj.insert("sim", faceInfo->sim); + QJsonArray rectArray; + rectArray.append(faceInfo->face.pos.x); + rectArray.append(faceInfo->face.pos.y); + rectArray.append(faceInfo->face.pos.width); + rectArray.append(faceInfo->face.pos.height); + faceInfoObj.insert("faceRect", rectArray); + + obj.insert("faceInfo", faceInfoObj); + + return obj; } diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index 999486e..f259312 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -2,7 +2,13 @@ #define CASICFACERECSTATE_H #include +#include +#include +#include +#include + #include "casic/face/CasicFaceInfo.h" +#include "utils/easyloggingpp/easylogging++.h" class CasicFaceRecState : public QObject { @@ -17,7 +23,7 @@ } void initRecognize(); - std::string toString(); + QString toString(); QJsonObject toJSON(); std::string recoginzeId; // 识别过程id @@ -31,6 +37,19 @@ qint8 noFaceCount = 0; // 连续没有找到人脸次数 float recogTimeLast = 0.0; // 识别成功耗时 + int state; // 识别状态 + + enum FaceRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_DETECT = 1, // 找到人脸 + REC_ANTI_SPOOFING = 2, // 活体检测结束 + REC_QUALIFY = 3, // 质量检测结束 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 人脸库比对成功 + REC_SEARCH_FAIL = 6 // 人脸库比对失败 + }; + private: CasicFaceRecState(); diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/device/face/CasicFaceRecState.cpp b/device/face/CasicFaceRecState.cpp index 3ba8470..7415a86 100644 --- a/device/face/CasicFaceRecState.cpp +++ b/device/face/CasicFaceRecState.cpp @@ -2,5 +2,56 @@ CasicFaceRecState::CasicFaceRecState() { + this->recoginzeId = "0"; + this->timeStamp = -1; + this->state = FaceRecStateName::REC_NOT_START; +} + +void CasicFaceRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noFaceCount = 0; + + LOG(DEBUG) << QString("[CasicFaceRecState][initRecognize] 人脸识别状态初始化").toStdString(); + + // 开始识别 + this->state = FaceRecStateName::REC_DETECT; +} + +QString CasicFaceRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + +QJsonObject CasicFaceRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromLocal8Bit(recoginzeId.data())); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noFaceCount", noFaceCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject faceInfoObj; + faceInfoObj.insert("hasFace", faceInfo->hasFace); + faceInfoObj.insert("reality", faceInfo->antiReality); + faceInfoObj.insert("clarity", faceInfo->antiClarity); + faceInfoObj.insert("sim", faceInfo->sim); + QJsonArray rectArray; + rectArray.append(faceInfo->face.pos.x); + rectArray.append(faceInfo->face.pos.y); + rectArray.append(faceInfo->face.pos.width); + rectArray.append(faceInfo->face.pos.height); + faceInfoObj.insert("faceRect", rectArray); + + obj.insert("faceInfo", faceInfoObj); + + return obj; } diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index 999486e..f259312 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -2,7 +2,13 @@ #define CASICFACERECSTATE_H #include +#include +#include +#include +#include + #include "casic/face/CasicFaceInfo.h" +#include "utils/easyloggingpp/easylogging++.h" class CasicFaceRecState : public QObject { @@ -17,7 +23,7 @@ } void initRecognize(); - std::string toString(); + QString toString(); QJsonObject toJSON(); std::string recoginzeId; // 识别过程id @@ -31,6 +37,19 @@ qint8 noFaceCount = 0; // 连续没有找到人脸次数 float recogTimeLast = 0.0; // 识别成功耗时 + int state; // 识别状态 + + enum FaceRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_DETECT = 1, // 找到人脸 + REC_ANTI_SPOOFING = 2, // 活体检测结束 + REC_QUALIFY = 3, // 质量检测结束 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 人脸库比对成功 + REC_SEARCH_FAIL = 6 // 人脸库比对失败 + }; + private: CasicFaceRecState(); diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp index 82caac5..ba078cc 100644 --- a/device/face/FaceDetectRegistProcess.cpp +++ b/device/face/FaceDetectRegistProcess.cpp @@ -1,72 +1,208 @@ #include "FaceDetectRegistProcess.h" -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QObject(parent) +FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) { - + this->working = false; + this->exit = false; } -void FaceDetectRegistProcess::faceDetect(cv::Mat faceMat) +FaceDetectRegistProcess::~FaceDetectRegistProcess() { - LOG(DEBUG) << "START FACE DETECT" << FACE_DETECT_FLAG; - - // 如果已经在人脸检测工作中则退出 - if (FACE_DETECT_FLAG == true) - { - LOG(DEBUG) << "ALREADY IN FACE DETECT PROCESS"; - return; - } - - // 开始人脸检测 - FACE_DETECT_FLAG = true; - LOG(DEBUG) << "START FACE DETECT"; - - // 调用人脸检测算法 - CasicFaceInfo faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceMat); - if (faceInfo.hasFace == false) - { - // 没有找到人脸进行如下处理 - - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; - return; - } - - // 继续进行后面的步骤 - // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 - casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); - faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); - // 活体检测判断为假脸 - if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸活体检测未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 判定为真实人脸, 开始质量评估 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); - - // 质量评估不为HIGH则返回 - if (faceInfo.quality.level != seeta::QualityLevel::HIGH) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸质量评估未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 调用算法提取特征值, 1024个字节的float数组 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); - - LOG(DEBUG) << QString("[faceDetect]特征提取成功").toStdString(); - - // 发送信号去进行比对, 执行后续业务逻辑 - emit this->extractFeatureSuccess(CasicFaceRecState::getInstance()); - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; + this->setWorking(false); } + +void FaceDetectRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceDetectRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceDetectRegistProcess::addOneTryCount() +{ + LOG(DEBUG) << QString("[addOneTryCount]已尝试次数[%1]").arg(CasicFaceRecState::getInstance().tryCount).toStdString(); + if (CasicFaceRecState::getInstance().tryCount < SettingConfig::getInstance().MAX_FACE_TRY_COUNT) + { + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + } else + { + CasicFaceRecState::getInstance().tryCount = 0; + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则线程暂停工作 + } +} + +void FaceDetectRegistProcess::addOneNoFaceCount() +{ + CasicFaceRecState::getInstance().noFaceCount++; + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + if (CasicFaceRecState::getInstance().noFaceCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT) + { + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则暂停工作 + } +} + +void FaceDetectRegistProcess::run() +{ + while (exit == false) + { + if (this->working == false) + { + this->msleep(100); // 100ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isFaceQueueEmpty() == true) + { + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(TRACE) << QString("[CasicFaceRegistThread][%1] 人脸图像栈空, 暂停200ms").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + + this->msleep(200); // 200ms后再判断 + continue; + } + + // 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicFaceRecState::getInstance().state != CasicFaceRecState::FaceRecStateName::REC_NOT_START) + { + LOG(DEBUG) << QString("[CasicFaceRegistThread] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicFaceRecState::getInstance().state).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出人脸图像栈中的一条数据 + CasicFaceInfo faceInfo = ProMemory::getInstance().popCasicFace(); + + // 调用人脸检测算法 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceInfo.matData); + + // 没有找到人脸则返回 + if (faceInfo.hasFace == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有多调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 没有找到人脸, 暂停200ms。重置识别状态为REC_NOT_START[%1]").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到人脸则开始一次识别 + if (CasicFaceRecState::getInstance().recoginzeId == "0") + { + CasicFaceRecState::getInstance().initRecognize(); + } + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + CasicFaceRecState::getInstance().tryCount++; // 尝试识别次数+1 + CasicFaceRecState::getInstance().noFaceCount = 0; // 找到脸 则将连续没找到脸的次数清零 + + // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 + casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); + faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); + + // 活体检测判断为假脸 + if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸活体检测未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过活体检测 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_ANTI_SPOOFING; + + // 判定为真实人脸, 开始质量评估 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); + + // 质量评估不为HIGH则返回 + if (faceInfo.quality.level != seeta::QualityLevel::HIGH) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸质量评估未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过质量评估 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_QUALIFY; + + // 调用算法提取特征值, 1024个字节的float数组 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); + + // 设置识别状态值=特征值提取 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_FEATURE_EXTRACT; + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + + // 裁剪识别到的人脸区域 + cv::Rect rect(faceInfo.face.pos.x, faceInfo.face.pos.y, faceInfo.face.pos.width, faceInfo.face.pos.height); + cv::Mat matRect = ImageUtil::MatImageRect(faceInfo.matData, rect, 100); + + QImage tempImg = ImageUtil::MatImageToQImage(matRect); + QByteArray ba; + QBuffer buf(&ba); + tempImg.save(&buf, "bmp"); + CasicFaceRecState::getInstance().imgBase64 = ba.toBase64(); + + LOG(DEBUG) << QString("[CasicFaceRegistThread] 特征提取成功[%1]").arg(CasicFaceRecState::getInstance().toString()).toStdString(); + + this->setWorking(false); + // 特征值提取成功的人脸 与人脸库中的人脸进行比较 + compareFaceInCollection(); + } +} + +void FaceDetectRegistProcess::compareFaceInCollection() +{ + ProMemory::getInstance().clearFaceQueue(); // 特征值提取成功, 开始在库中匹配, 清除人脸图像栈 + QVector faceFeatures = ProMemory::getInstance().getFaceFeatures(); + if (faceFeatures.isEmpty() == true) + { + // 没有人脸特征值数据, 发送信号可以入库 + emit successCaptureFace(""); + } else + { + for (int i = 0; i < faceFeatures.size(); i++) + { + QVariantMap ffMap = faceFeatures.at(i); + + QByteArray ffByteArr = ffMap.value("face_code").toByteArray(); + float ffArr[1024]; + for (int i = 0; i < 1024; i++) + { + QByteArray tempBa; + tempBa.append(ffByteArr.at(i*4)).append(ffByteArr.at(i*4 + 1)).append(ffByteArr.at(i*4 + 2)).append(ffByteArr.at(i*4 + 3)); + ffArr[i] = ByteUtil::binToFloat(tempBa); + } + + float sim = casic::face::CasicFaceInterface::getInstance().faceSimCalculate(CasicFaceRecState::getInstance().faceInfo->feature, ffArr); + + LOG(DEBUG) << QString("[compareFaceInCollection] 特征值比对结果[%1]").arg(sim).toStdString(); + if (sim > 0.62) + { + CasicFaceRecState::getInstance().faceInfo->sim = sim; + CasicFaceRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + CasicFaceRecState::getInstance().recogTimeLast = (float) ((CasicFaceRecState::getInstance().timeStampSucc - CasicFaceRecState::getInstance().timeStamp) / 1000.0); + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_SEARCH_SUCC; + + // 找到匹配的人脸图像 + emit successCaptureFace(ffMap.value("person_id").toString()); + return ; + } + } + + // 没有找到匹配的人脸 + emit successCaptureFace(""); + } +} + diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/device/face/CasicFaceRecState.cpp b/device/face/CasicFaceRecState.cpp index 3ba8470..7415a86 100644 --- a/device/face/CasicFaceRecState.cpp +++ b/device/face/CasicFaceRecState.cpp @@ -2,5 +2,56 @@ CasicFaceRecState::CasicFaceRecState() { + this->recoginzeId = "0"; + this->timeStamp = -1; + this->state = FaceRecStateName::REC_NOT_START; +} + +void CasicFaceRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noFaceCount = 0; + + LOG(DEBUG) << QString("[CasicFaceRecState][initRecognize] 人脸识别状态初始化").toStdString(); + + // 开始识别 + this->state = FaceRecStateName::REC_DETECT; +} + +QString CasicFaceRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + +QJsonObject CasicFaceRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromLocal8Bit(recoginzeId.data())); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noFaceCount", noFaceCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject faceInfoObj; + faceInfoObj.insert("hasFace", faceInfo->hasFace); + faceInfoObj.insert("reality", faceInfo->antiReality); + faceInfoObj.insert("clarity", faceInfo->antiClarity); + faceInfoObj.insert("sim", faceInfo->sim); + QJsonArray rectArray; + rectArray.append(faceInfo->face.pos.x); + rectArray.append(faceInfo->face.pos.y); + rectArray.append(faceInfo->face.pos.width); + rectArray.append(faceInfo->face.pos.height); + faceInfoObj.insert("faceRect", rectArray); + + obj.insert("faceInfo", faceInfoObj); + + return obj; } diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index 999486e..f259312 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -2,7 +2,13 @@ #define CASICFACERECSTATE_H #include +#include +#include +#include +#include + #include "casic/face/CasicFaceInfo.h" +#include "utils/easyloggingpp/easylogging++.h" class CasicFaceRecState : public QObject { @@ -17,7 +23,7 @@ } void initRecognize(); - std::string toString(); + QString toString(); QJsonObject toJSON(); std::string recoginzeId; // 识别过程id @@ -31,6 +37,19 @@ qint8 noFaceCount = 0; // 连续没有找到人脸次数 float recogTimeLast = 0.0; // 识别成功耗时 + int state; // 识别状态 + + enum FaceRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_DETECT = 1, // 找到人脸 + REC_ANTI_SPOOFING = 2, // 活体检测结束 + REC_QUALIFY = 3, // 质量检测结束 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 人脸库比对成功 + REC_SEARCH_FAIL = 6 // 人脸库比对失败 + }; + private: CasicFaceRecState(); diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp index 82caac5..ba078cc 100644 --- a/device/face/FaceDetectRegistProcess.cpp +++ b/device/face/FaceDetectRegistProcess.cpp @@ -1,72 +1,208 @@ #include "FaceDetectRegistProcess.h" -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QObject(parent) +FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) { - + this->working = false; + this->exit = false; } -void FaceDetectRegistProcess::faceDetect(cv::Mat faceMat) +FaceDetectRegistProcess::~FaceDetectRegistProcess() { - LOG(DEBUG) << "START FACE DETECT" << FACE_DETECT_FLAG; - - // 如果已经在人脸检测工作中则退出 - if (FACE_DETECT_FLAG == true) - { - LOG(DEBUG) << "ALREADY IN FACE DETECT PROCESS"; - return; - } - - // 开始人脸检测 - FACE_DETECT_FLAG = true; - LOG(DEBUG) << "START FACE DETECT"; - - // 调用人脸检测算法 - CasicFaceInfo faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceMat); - if (faceInfo.hasFace == false) - { - // 没有找到人脸进行如下处理 - - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; - return; - } - - // 继续进行后面的步骤 - // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 - casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); - faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); - // 活体检测判断为假脸 - if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸活体检测未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 判定为真实人脸, 开始质量评估 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); - - // 质量评估不为HIGH则返回 - if (faceInfo.quality.level != seeta::QualityLevel::HIGH) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸质量评估未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 调用算法提取特征值, 1024个字节的float数组 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); - - LOG(DEBUG) << QString("[faceDetect]特征提取成功").toStdString(); - - // 发送信号去进行比对, 执行后续业务逻辑 - emit this->extractFeatureSuccess(CasicFaceRecState::getInstance()); - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; + this->setWorking(false); } + +void FaceDetectRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceDetectRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceDetectRegistProcess::addOneTryCount() +{ + LOG(DEBUG) << QString("[addOneTryCount]已尝试次数[%1]").arg(CasicFaceRecState::getInstance().tryCount).toStdString(); + if (CasicFaceRecState::getInstance().tryCount < SettingConfig::getInstance().MAX_FACE_TRY_COUNT) + { + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + } else + { + CasicFaceRecState::getInstance().tryCount = 0; + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则线程暂停工作 + } +} + +void FaceDetectRegistProcess::addOneNoFaceCount() +{ + CasicFaceRecState::getInstance().noFaceCount++; + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + if (CasicFaceRecState::getInstance().noFaceCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT) + { + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则暂停工作 + } +} + +void FaceDetectRegistProcess::run() +{ + while (exit == false) + { + if (this->working == false) + { + this->msleep(100); // 100ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isFaceQueueEmpty() == true) + { + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(TRACE) << QString("[CasicFaceRegistThread][%1] 人脸图像栈空, 暂停200ms").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + + this->msleep(200); // 200ms后再判断 + continue; + } + + // 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicFaceRecState::getInstance().state != CasicFaceRecState::FaceRecStateName::REC_NOT_START) + { + LOG(DEBUG) << QString("[CasicFaceRegistThread] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicFaceRecState::getInstance().state).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出人脸图像栈中的一条数据 + CasicFaceInfo faceInfo = ProMemory::getInstance().popCasicFace(); + + // 调用人脸检测算法 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceInfo.matData); + + // 没有找到人脸则返回 + if (faceInfo.hasFace == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有多调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 没有找到人脸, 暂停200ms。重置识别状态为REC_NOT_START[%1]").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到人脸则开始一次识别 + if (CasicFaceRecState::getInstance().recoginzeId == "0") + { + CasicFaceRecState::getInstance().initRecognize(); + } + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + CasicFaceRecState::getInstance().tryCount++; // 尝试识别次数+1 + CasicFaceRecState::getInstance().noFaceCount = 0; // 找到脸 则将连续没找到脸的次数清零 + + // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 + casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); + faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); + + // 活体检测判断为假脸 + if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸活体检测未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过活体检测 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_ANTI_SPOOFING; + + // 判定为真实人脸, 开始质量评估 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); + + // 质量评估不为HIGH则返回 + if (faceInfo.quality.level != seeta::QualityLevel::HIGH) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸质量评估未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过质量评估 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_QUALIFY; + + // 调用算法提取特征值, 1024个字节的float数组 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); + + // 设置识别状态值=特征值提取 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_FEATURE_EXTRACT; + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + + // 裁剪识别到的人脸区域 + cv::Rect rect(faceInfo.face.pos.x, faceInfo.face.pos.y, faceInfo.face.pos.width, faceInfo.face.pos.height); + cv::Mat matRect = ImageUtil::MatImageRect(faceInfo.matData, rect, 100); + + QImage tempImg = ImageUtil::MatImageToQImage(matRect); + QByteArray ba; + QBuffer buf(&ba); + tempImg.save(&buf, "bmp"); + CasicFaceRecState::getInstance().imgBase64 = ba.toBase64(); + + LOG(DEBUG) << QString("[CasicFaceRegistThread] 特征提取成功[%1]").arg(CasicFaceRecState::getInstance().toString()).toStdString(); + + this->setWorking(false); + // 特征值提取成功的人脸 与人脸库中的人脸进行比较 + compareFaceInCollection(); + } +} + +void FaceDetectRegistProcess::compareFaceInCollection() +{ + ProMemory::getInstance().clearFaceQueue(); // 特征值提取成功, 开始在库中匹配, 清除人脸图像栈 + QVector faceFeatures = ProMemory::getInstance().getFaceFeatures(); + if (faceFeatures.isEmpty() == true) + { + // 没有人脸特征值数据, 发送信号可以入库 + emit successCaptureFace(""); + } else + { + for (int i = 0; i < faceFeatures.size(); i++) + { + QVariantMap ffMap = faceFeatures.at(i); + + QByteArray ffByteArr = ffMap.value("face_code").toByteArray(); + float ffArr[1024]; + for (int i = 0; i < 1024; i++) + { + QByteArray tempBa; + tempBa.append(ffByteArr.at(i*4)).append(ffByteArr.at(i*4 + 1)).append(ffByteArr.at(i*4 + 2)).append(ffByteArr.at(i*4 + 3)); + ffArr[i] = ByteUtil::binToFloat(tempBa); + } + + float sim = casic::face::CasicFaceInterface::getInstance().faceSimCalculate(CasicFaceRecState::getInstance().faceInfo->feature, ffArr); + + LOG(DEBUG) << QString("[compareFaceInCollection] 特征值比对结果[%1]").arg(sim).toStdString(); + if (sim > 0.62) + { + CasicFaceRecState::getInstance().faceInfo->sim = sim; + CasicFaceRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + CasicFaceRecState::getInstance().recogTimeLast = (float) ((CasicFaceRecState::getInstance().timeStampSucc - CasicFaceRecState::getInstance().timeStamp) / 1000.0); + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_SEARCH_SUCC; + + // 找到匹配的人脸图像 + emit successCaptureFace(ffMap.value("person_id").toString()); + return ; + } + } + + // 没有找到匹配的人脸 + emit successCaptureFace(""); + } +} + diff --git a/device/face/FaceDetectRegistProcess.h b/device/face/FaceDetectRegistProcess.h index 5d42bbe..4df7708 100644 --- a/device/face/FaceDetectRegistProcess.h +++ b/device/face/FaceDetectRegistProcess.h @@ -2,26 +2,48 @@ #define FACEDETECTREGISTPROCESS_H #include +#include #include "opencv2/opencv.hpp" #include "utils/easyloggingpp/easylogging++.h" +#include "utils/ImageUtil.h" +#include "utils/SettingConfig.h" +#include "utils/ByteUtil.h" #include "CasicFaceRecState.h" +#include "casic/ProMemory.h" #include "casic/face/CasicFaceInterface.h" static bool FACE_DETECT_FLAG = false; -class FaceDetectRegistProcess : public QObject +class FaceDetectRegistProcess : public QThread { Q_OBJECT public: explicit FaceDetectRegistProcess(QObject *parent = nullptr); + ~FaceDetectRegistProcess(); -public slots: - void faceDetect(cv::Mat faceMat); + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoFaceCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + void compareFaceInCollection(); + + volatile bool working; + volatile bool exit; signals: - void extractFeatureSuccess(CasicFaceRecState& faceRecState); + void extractFeatureSuccess(); + void failedCaptureFace(); + void successCaptureFace(QString personId); + void findSimFace(QString personId); }; diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/device/face/CasicFaceRecState.cpp b/device/face/CasicFaceRecState.cpp index 3ba8470..7415a86 100644 --- a/device/face/CasicFaceRecState.cpp +++ b/device/face/CasicFaceRecState.cpp @@ -2,5 +2,56 @@ CasicFaceRecState::CasicFaceRecState() { + this->recoginzeId = "0"; + this->timeStamp = -1; + this->state = FaceRecStateName::REC_NOT_START; +} + +void CasicFaceRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noFaceCount = 0; + + LOG(DEBUG) << QString("[CasicFaceRecState][initRecognize] 人脸识别状态初始化").toStdString(); + + // 开始识别 + this->state = FaceRecStateName::REC_DETECT; +} + +QString CasicFaceRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + +QJsonObject CasicFaceRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromLocal8Bit(recoginzeId.data())); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noFaceCount", noFaceCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject faceInfoObj; + faceInfoObj.insert("hasFace", faceInfo->hasFace); + faceInfoObj.insert("reality", faceInfo->antiReality); + faceInfoObj.insert("clarity", faceInfo->antiClarity); + faceInfoObj.insert("sim", faceInfo->sim); + QJsonArray rectArray; + rectArray.append(faceInfo->face.pos.x); + rectArray.append(faceInfo->face.pos.y); + rectArray.append(faceInfo->face.pos.width); + rectArray.append(faceInfo->face.pos.height); + faceInfoObj.insert("faceRect", rectArray); + + obj.insert("faceInfo", faceInfoObj); + + return obj; } diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index 999486e..f259312 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -2,7 +2,13 @@ #define CASICFACERECSTATE_H #include +#include +#include +#include +#include + #include "casic/face/CasicFaceInfo.h" +#include "utils/easyloggingpp/easylogging++.h" class CasicFaceRecState : public QObject { @@ -17,7 +23,7 @@ } void initRecognize(); - std::string toString(); + QString toString(); QJsonObject toJSON(); std::string recoginzeId; // 识别过程id @@ -31,6 +37,19 @@ qint8 noFaceCount = 0; // 连续没有找到人脸次数 float recogTimeLast = 0.0; // 识别成功耗时 + int state; // 识别状态 + + enum FaceRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_DETECT = 1, // 找到人脸 + REC_ANTI_SPOOFING = 2, // 活体检测结束 + REC_QUALIFY = 3, // 质量检测结束 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 人脸库比对成功 + REC_SEARCH_FAIL = 6 // 人脸库比对失败 + }; + private: CasicFaceRecState(); diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp index 82caac5..ba078cc 100644 --- a/device/face/FaceDetectRegistProcess.cpp +++ b/device/face/FaceDetectRegistProcess.cpp @@ -1,72 +1,208 @@ #include "FaceDetectRegistProcess.h" -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QObject(parent) +FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) { - + this->working = false; + this->exit = false; } -void FaceDetectRegistProcess::faceDetect(cv::Mat faceMat) +FaceDetectRegistProcess::~FaceDetectRegistProcess() { - LOG(DEBUG) << "START FACE DETECT" << FACE_DETECT_FLAG; - - // 如果已经在人脸检测工作中则退出 - if (FACE_DETECT_FLAG == true) - { - LOG(DEBUG) << "ALREADY IN FACE DETECT PROCESS"; - return; - } - - // 开始人脸检测 - FACE_DETECT_FLAG = true; - LOG(DEBUG) << "START FACE DETECT"; - - // 调用人脸检测算法 - CasicFaceInfo faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceMat); - if (faceInfo.hasFace == false) - { - // 没有找到人脸进行如下处理 - - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; - return; - } - - // 继续进行后面的步骤 - // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 - casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); - faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); - // 活体检测判断为假脸 - if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸活体检测未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 判定为真实人脸, 开始质量评估 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); - - // 质量评估不为HIGH则返回 - if (faceInfo.quality.level != seeta::QualityLevel::HIGH) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸质量评估未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 调用算法提取特征值, 1024个字节的float数组 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); - - LOG(DEBUG) << QString("[faceDetect]特征提取成功").toStdString(); - - // 发送信号去进行比对, 执行后续业务逻辑 - emit this->extractFeatureSuccess(CasicFaceRecState::getInstance()); - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; + this->setWorking(false); } + +void FaceDetectRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceDetectRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceDetectRegistProcess::addOneTryCount() +{ + LOG(DEBUG) << QString("[addOneTryCount]已尝试次数[%1]").arg(CasicFaceRecState::getInstance().tryCount).toStdString(); + if (CasicFaceRecState::getInstance().tryCount < SettingConfig::getInstance().MAX_FACE_TRY_COUNT) + { + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + } else + { + CasicFaceRecState::getInstance().tryCount = 0; + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则线程暂停工作 + } +} + +void FaceDetectRegistProcess::addOneNoFaceCount() +{ + CasicFaceRecState::getInstance().noFaceCount++; + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + if (CasicFaceRecState::getInstance().noFaceCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT) + { + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则暂停工作 + } +} + +void FaceDetectRegistProcess::run() +{ + while (exit == false) + { + if (this->working == false) + { + this->msleep(100); // 100ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isFaceQueueEmpty() == true) + { + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(TRACE) << QString("[CasicFaceRegistThread][%1] 人脸图像栈空, 暂停200ms").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + + this->msleep(200); // 200ms后再判断 + continue; + } + + // 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicFaceRecState::getInstance().state != CasicFaceRecState::FaceRecStateName::REC_NOT_START) + { + LOG(DEBUG) << QString("[CasicFaceRegistThread] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicFaceRecState::getInstance().state).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出人脸图像栈中的一条数据 + CasicFaceInfo faceInfo = ProMemory::getInstance().popCasicFace(); + + // 调用人脸检测算法 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceInfo.matData); + + // 没有找到人脸则返回 + if (faceInfo.hasFace == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有多调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 没有找到人脸, 暂停200ms。重置识别状态为REC_NOT_START[%1]").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到人脸则开始一次识别 + if (CasicFaceRecState::getInstance().recoginzeId == "0") + { + CasicFaceRecState::getInstance().initRecognize(); + } + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + CasicFaceRecState::getInstance().tryCount++; // 尝试识别次数+1 + CasicFaceRecState::getInstance().noFaceCount = 0; // 找到脸 则将连续没找到脸的次数清零 + + // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 + casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); + faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); + + // 活体检测判断为假脸 + if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸活体检测未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过活体检测 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_ANTI_SPOOFING; + + // 判定为真实人脸, 开始质量评估 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); + + // 质量评估不为HIGH则返回 + if (faceInfo.quality.level != seeta::QualityLevel::HIGH) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸质量评估未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过质量评估 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_QUALIFY; + + // 调用算法提取特征值, 1024个字节的float数组 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); + + // 设置识别状态值=特征值提取 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_FEATURE_EXTRACT; + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + + // 裁剪识别到的人脸区域 + cv::Rect rect(faceInfo.face.pos.x, faceInfo.face.pos.y, faceInfo.face.pos.width, faceInfo.face.pos.height); + cv::Mat matRect = ImageUtil::MatImageRect(faceInfo.matData, rect, 100); + + QImage tempImg = ImageUtil::MatImageToQImage(matRect); + QByteArray ba; + QBuffer buf(&ba); + tempImg.save(&buf, "bmp"); + CasicFaceRecState::getInstance().imgBase64 = ba.toBase64(); + + LOG(DEBUG) << QString("[CasicFaceRegistThread] 特征提取成功[%1]").arg(CasicFaceRecState::getInstance().toString()).toStdString(); + + this->setWorking(false); + // 特征值提取成功的人脸 与人脸库中的人脸进行比较 + compareFaceInCollection(); + } +} + +void FaceDetectRegistProcess::compareFaceInCollection() +{ + ProMemory::getInstance().clearFaceQueue(); // 特征值提取成功, 开始在库中匹配, 清除人脸图像栈 + QVector faceFeatures = ProMemory::getInstance().getFaceFeatures(); + if (faceFeatures.isEmpty() == true) + { + // 没有人脸特征值数据, 发送信号可以入库 + emit successCaptureFace(""); + } else + { + for (int i = 0; i < faceFeatures.size(); i++) + { + QVariantMap ffMap = faceFeatures.at(i); + + QByteArray ffByteArr = ffMap.value("face_code").toByteArray(); + float ffArr[1024]; + for (int i = 0; i < 1024; i++) + { + QByteArray tempBa; + tempBa.append(ffByteArr.at(i*4)).append(ffByteArr.at(i*4 + 1)).append(ffByteArr.at(i*4 + 2)).append(ffByteArr.at(i*4 + 3)); + ffArr[i] = ByteUtil::binToFloat(tempBa); + } + + float sim = casic::face::CasicFaceInterface::getInstance().faceSimCalculate(CasicFaceRecState::getInstance().faceInfo->feature, ffArr); + + LOG(DEBUG) << QString("[compareFaceInCollection] 特征值比对结果[%1]").arg(sim).toStdString(); + if (sim > 0.62) + { + CasicFaceRecState::getInstance().faceInfo->sim = sim; + CasicFaceRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + CasicFaceRecState::getInstance().recogTimeLast = (float) ((CasicFaceRecState::getInstance().timeStampSucc - CasicFaceRecState::getInstance().timeStamp) / 1000.0); + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_SEARCH_SUCC; + + // 找到匹配的人脸图像 + emit successCaptureFace(ffMap.value("person_id").toString()); + return ; + } + } + + // 没有找到匹配的人脸 + emit successCaptureFace(""); + } +} + diff --git a/device/face/FaceDetectRegistProcess.h b/device/face/FaceDetectRegistProcess.h index 5d42bbe..4df7708 100644 --- a/device/face/FaceDetectRegistProcess.h +++ b/device/face/FaceDetectRegistProcess.h @@ -2,26 +2,48 @@ #define FACEDETECTREGISTPROCESS_H #include +#include #include "opencv2/opencv.hpp" #include "utils/easyloggingpp/easylogging++.h" +#include "utils/ImageUtil.h" +#include "utils/SettingConfig.h" +#include "utils/ByteUtil.h" #include "CasicFaceRecState.h" +#include "casic/ProMemory.h" #include "casic/face/CasicFaceInterface.h" static bool FACE_DETECT_FLAG = false; -class FaceDetectRegistProcess : public QObject +class FaceDetectRegistProcess : public QThread { Q_OBJECT public: explicit FaceDetectRegistProcess(QObject *parent = nullptr); + ~FaceDetectRegistProcess(); -public slots: - void faceDetect(cv::Mat faceMat); + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoFaceCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + void compareFaceInCollection(); + + volatile bool working; + volatile bool exit; signals: - void extractFeatureSuccess(CasicFaceRecState& faceRecState); + void extractFeatureSuccess(); + void failedCaptureFace(); + void successCaptureFace(QString personId); + void findSimFace(QString personId); }; diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/device/face/CasicFaceRecState.cpp b/device/face/CasicFaceRecState.cpp index 3ba8470..7415a86 100644 --- a/device/face/CasicFaceRecState.cpp +++ b/device/face/CasicFaceRecState.cpp @@ -2,5 +2,56 @@ CasicFaceRecState::CasicFaceRecState() { + this->recoginzeId = "0"; + this->timeStamp = -1; + this->state = FaceRecStateName::REC_NOT_START; +} + +void CasicFaceRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noFaceCount = 0; + + LOG(DEBUG) << QString("[CasicFaceRecState][initRecognize] 人脸识别状态初始化").toStdString(); + + // 开始识别 + this->state = FaceRecStateName::REC_DETECT; +} + +QString CasicFaceRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + +QJsonObject CasicFaceRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromLocal8Bit(recoginzeId.data())); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noFaceCount", noFaceCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject faceInfoObj; + faceInfoObj.insert("hasFace", faceInfo->hasFace); + faceInfoObj.insert("reality", faceInfo->antiReality); + faceInfoObj.insert("clarity", faceInfo->antiClarity); + faceInfoObj.insert("sim", faceInfo->sim); + QJsonArray rectArray; + rectArray.append(faceInfo->face.pos.x); + rectArray.append(faceInfo->face.pos.y); + rectArray.append(faceInfo->face.pos.width); + rectArray.append(faceInfo->face.pos.height); + faceInfoObj.insert("faceRect", rectArray); + + obj.insert("faceInfo", faceInfoObj); + + return obj; } diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index 999486e..f259312 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -2,7 +2,13 @@ #define CASICFACERECSTATE_H #include +#include +#include +#include +#include + #include "casic/face/CasicFaceInfo.h" +#include "utils/easyloggingpp/easylogging++.h" class CasicFaceRecState : public QObject { @@ -17,7 +23,7 @@ } void initRecognize(); - std::string toString(); + QString toString(); QJsonObject toJSON(); std::string recoginzeId; // 识别过程id @@ -31,6 +37,19 @@ qint8 noFaceCount = 0; // 连续没有找到人脸次数 float recogTimeLast = 0.0; // 识别成功耗时 + int state; // 识别状态 + + enum FaceRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_DETECT = 1, // 找到人脸 + REC_ANTI_SPOOFING = 2, // 活体检测结束 + REC_QUALIFY = 3, // 质量检测结束 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 人脸库比对成功 + REC_SEARCH_FAIL = 6 // 人脸库比对失败 + }; + private: CasicFaceRecState(); diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp index 82caac5..ba078cc 100644 --- a/device/face/FaceDetectRegistProcess.cpp +++ b/device/face/FaceDetectRegistProcess.cpp @@ -1,72 +1,208 @@ #include "FaceDetectRegistProcess.h" -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QObject(parent) +FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) { - + this->working = false; + this->exit = false; } -void FaceDetectRegistProcess::faceDetect(cv::Mat faceMat) +FaceDetectRegistProcess::~FaceDetectRegistProcess() { - LOG(DEBUG) << "START FACE DETECT" << FACE_DETECT_FLAG; - - // 如果已经在人脸检测工作中则退出 - if (FACE_DETECT_FLAG == true) - { - LOG(DEBUG) << "ALREADY IN FACE DETECT PROCESS"; - return; - } - - // 开始人脸检测 - FACE_DETECT_FLAG = true; - LOG(DEBUG) << "START FACE DETECT"; - - // 调用人脸检测算法 - CasicFaceInfo faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceMat); - if (faceInfo.hasFace == false) - { - // 没有找到人脸进行如下处理 - - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; - return; - } - - // 继续进行后面的步骤 - // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 - casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); - faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); - // 活体检测判断为假脸 - if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸活体检测未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 判定为真实人脸, 开始质量评估 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); - - // 质量评估不为HIGH则返回 - if (faceInfo.quality.level != seeta::QualityLevel::HIGH) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸质量评估未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 调用算法提取特征值, 1024个字节的float数组 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); - - LOG(DEBUG) << QString("[faceDetect]特征提取成功").toStdString(); - - // 发送信号去进行比对, 执行后续业务逻辑 - emit this->extractFeatureSuccess(CasicFaceRecState::getInstance()); - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; + this->setWorking(false); } + +void FaceDetectRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceDetectRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceDetectRegistProcess::addOneTryCount() +{ + LOG(DEBUG) << QString("[addOneTryCount]已尝试次数[%1]").arg(CasicFaceRecState::getInstance().tryCount).toStdString(); + if (CasicFaceRecState::getInstance().tryCount < SettingConfig::getInstance().MAX_FACE_TRY_COUNT) + { + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + } else + { + CasicFaceRecState::getInstance().tryCount = 0; + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则线程暂停工作 + } +} + +void FaceDetectRegistProcess::addOneNoFaceCount() +{ + CasicFaceRecState::getInstance().noFaceCount++; + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + if (CasicFaceRecState::getInstance().noFaceCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT) + { + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则暂停工作 + } +} + +void FaceDetectRegistProcess::run() +{ + while (exit == false) + { + if (this->working == false) + { + this->msleep(100); // 100ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isFaceQueueEmpty() == true) + { + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(TRACE) << QString("[CasicFaceRegistThread][%1] 人脸图像栈空, 暂停200ms").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + + this->msleep(200); // 200ms后再判断 + continue; + } + + // 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicFaceRecState::getInstance().state != CasicFaceRecState::FaceRecStateName::REC_NOT_START) + { + LOG(DEBUG) << QString("[CasicFaceRegistThread] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicFaceRecState::getInstance().state).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出人脸图像栈中的一条数据 + CasicFaceInfo faceInfo = ProMemory::getInstance().popCasicFace(); + + // 调用人脸检测算法 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceInfo.matData); + + // 没有找到人脸则返回 + if (faceInfo.hasFace == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有多调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 没有找到人脸, 暂停200ms。重置识别状态为REC_NOT_START[%1]").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到人脸则开始一次识别 + if (CasicFaceRecState::getInstance().recoginzeId == "0") + { + CasicFaceRecState::getInstance().initRecognize(); + } + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + CasicFaceRecState::getInstance().tryCount++; // 尝试识别次数+1 + CasicFaceRecState::getInstance().noFaceCount = 0; // 找到脸 则将连续没找到脸的次数清零 + + // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 + casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); + faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); + + // 活体检测判断为假脸 + if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸活体检测未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过活体检测 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_ANTI_SPOOFING; + + // 判定为真实人脸, 开始质量评估 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); + + // 质量评估不为HIGH则返回 + if (faceInfo.quality.level != seeta::QualityLevel::HIGH) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸质量评估未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过质量评估 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_QUALIFY; + + // 调用算法提取特征值, 1024个字节的float数组 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); + + // 设置识别状态值=特征值提取 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_FEATURE_EXTRACT; + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + + // 裁剪识别到的人脸区域 + cv::Rect rect(faceInfo.face.pos.x, faceInfo.face.pos.y, faceInfo.face.pos.width, faceInfo.face.pos.height); + cv::Mat matRect = ImageUtil::MatImageRect(faceInfo.matData, rect, 100); + + QImage tempImg = ImageUtil::MatImageToQImage(matRect); + QByteArray ba; + QBuffer buf(&ba); + tempImg.save(&buf, "bmp"); + CasicFaceRecState::getInstance().imgBase64 = ba.toBase64(); + + LOG(DEBUG) << QString("[CasicFaceRegistThread] 特征提取成功[%1]").arg(CasicFaceRecState::getInstance().toString()).toStdString(); + + this->setWorking(false); + // 特征值提取成功的人脸 与人脸库中的人脸进行比较 + compareFaceInCollection(); + } +} + +void FaceDetectRegistProcess::compareFaceInCollection() +{ + ProMemory::getInstance().clearFaceQueue(); // 特征值提取成功, 开始在库中匹配, 清除人脸图像栈 + QVector faceFeatures = ProMemory::getInstance().getFaceFeatures(); + if (faceFeatures.isEmpty() == true) + { + // 没有人脸特征值数据, 发送信号可以入库 + emit successCaptureFace(""); + } else + { + for (int i = 0; i < faceFeatures.size(); i++) + { + QVariantMap ffMap = faceFeatures.at(i); + + QByteArray ffByteArr = ffMap.value("face_code").toByteArray(); + float ffArr[1024]; + for (int i = 0; i < 1024; i++) + { + QByteArray tempBa; + tempBa.append(ffByteArr.at(i*4)).append(ffByteArr.at(i*4 + 1)).append(ffByteArr.at(i*4 + 2)).append(ffByteArr.at(i*4 + 3)); + ffArr[i] = ByteUtil::binToFloat(tempBa); + } + + float sim = casic::face::CasicFaceInterface::getInstance().faceSimCalculate(CasicFaceRecState::getInstance().faceInfo->feature, ffArr); + + LOG(DEBUG) << QString("[compareFaceInCollection] 特征值比对结果[%1]").arg(sim).toStdString(); + if (sim > 0.62) + { + CasicFaceRecState::getInstance().faceInfo->sim = sim; + CasicFaceRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + CasicFaceRecState::getInstance().recogTimeLast = (float) ((CasicFaceRecState::getInstance().timeStampSucc - CasicFaceRecState::getInstance().timeStamp) / 1000.0); + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_SEARCH_SUCC; + + // 找到匹配的人脸图像 + emit successCaptureFace(ffMap.value("person_id").toString()); + return ; + } + } + + // 没有找到匹配的人脸 + emit successCaptureFace(""); + } +} + diff --git a/device/face/FaceDetectRegistProcess.h b/device/face/FaceDetectRegistProcess.h index 5d42bbe..4df7708 100644 --- a/device/face/FaceDetectRegistProcess.h +++ b/device/face/FaceDetectRegistProcess.h @@ -2,26 +2,48 @@ #define FACEDETECTREGISTPROCESS_H #include +#include #include "opencv2/opencv.hpp" #include "utils/easyloggingpp/easylogging++.h" +#include "utils/ImageUtil.h" +#include "utils/SettingConfig.h" +#include "utils/ByteUtil.h" #include "CasicFaceRecState.h" +#include "casic/ProMemory.h" #include "casic/face/CasicFaceInterface.h" static bool FACE_DETECT_FLAG = false; -class FaceDetectRegistProcess : public QObject +class FaceDetectRegistProcess : public QThread { Q_OBJECT public: explicit FaceDetectRegistProcess(QObject *parent = nullptr); + ~FaceDetectRegistProcess(); -public slots: - void faceDetect(cv::Mat faceMat); + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoFaceCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + void compareFaceInCollection(); + + volatile bool working; + volatile bool exit; signals: - void extractFeatureSuccess(CasicFaceRecState& faceRecState); + void extractFeatureSuccess(); + void failedCaptureFace(); + void successCaptureFace(QString personId); + void findSimFace(QString personId); }; diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/device/face/CasicFaceRecState.cpp b/device/face/CasicFaceRecState.cpp index 3ba8470..7415a86 100644 --- a/device/face/CasicFaceRecState.cpp +++ b/device/face/CasicFaceRecState.cpp @@ -2,5 +2,56 @@ CasicFaceRecState::CasicFaceRecState() { + this->recoginzeId = "0"; + this->timeStamp = -1; + this->state = FaceRecStateName::REC_NOT_START; +} + +void CasicFaceRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noFaceCount = 0; + + LOG(DEBUG) << QString("[CasicFaceRecState][initRecognize] 人脸识别状态初始化").toStdString(); + + // 开始识别 + this->state = FaceRecStateName::REC_DETECT; +} + +QString CasicFaceRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + +QJsonObject CasicFaceRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromLocal8Bit(recoginzeId.data())); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noFaceCount", noFaceCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject faceInfoObj; + faceInfoObj.insert("hasFace", faceInfo->hasFace); + faceInfoObj.insert("reality", faceInfo->antiReality); + faceInfoObj.insert("clarity", faceInfo->antiClarity); + faceInfoObj.insert("sim", faceInfo->sim); + QJsonArray rectArray; + rectArray.append(faceInfo->face.pos.x); + rectArray.append(faceInfo->face.pos.y); + rectArray.append(faceInfo->face.pos.width); + rectArray.append(faceInfo->face.pos.height); + faceInfoObj.insert("faceRect", rectArray); + + obj.insert("faceInfo", faceInfoObj); + + return obj; } diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index 999486e..f259312 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -2,7 +2,13 @@ #define CASICFACERECSTATE_H #include +#include +#include +#include +#include + #include "casic/face/CasicFaceInfo.h" +#include "utils/easyloggingpp/easylogging++.h" class CasicFaceRecState : public QObject { @@ -17,7 +23,7 @@ } void initRecognize(); - std::string toString(); + QString toString(); QJsonObject toJSON(); std::string recoginzeId; // 识别过程id @@ -31,6 +37,19 @@ qint8 noFaceCount = 0; // 连续没有找到人脸次数 float recogTimeLast = 0.0; // 识别成功耗时 + int state; // 识别状态 + + enum FaceRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_DETECT = 1, // 找到人脸 + REC_ANTI_SPOOFING = 2, // 活体检测结束 + REC_QUALIFY = 3, // 质量检测结束 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 人脸库比对成功 + REC_SEARCH_FAIL = 6 // 人脸库比对失败 + }; + private: CasicFaceRecState(); diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp index 82caac5..ba078cc 100644 --- a/device/face/FaceDetectRegistProcess.cpp +++ b/device/face/FaceDetectRegistProcess.cpp @@ -1,72 +1,208 @@ #include "FaceDetectRegistProcess.h" -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QObject(parent) +FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) { - + this->working = false; + this->exit = false; } -void FaceDetectRegistProcess::faceDetect(cv::Mat faceMat) +FaceDetectRegistProcess::~FaceDetectRegistProcess() { - LOG(DEBUG) << "START FACE DETECT" << FACE_DETECT_FLAG; - - // 如果已经在人脸检测工作中则退出 - if (FACE_DETECT_FLAG == true) - { - LOG(DEBUG) << "ALREADY IN FACE DETECT PROCESS"; - return; - } - - // 开始人脸检测 - FACE_DETECT_FLAG = true; - LOG(DEBUG) << "START FACE DETECT"; - - // 调用人脸检测算法 - CasicFaceInfo faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceMat); - if (faceInfo.hasFace == false) - { - // 没有找到人脸进行如下处理 - - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; - return; - } - - // 继续进行后面的步骤 - // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 - casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); - faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); - // 活体检测判断为假脸 - if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸活体检测未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 判定为真实人脸, 开始质量评估 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); - - // 质量评估不为HIGH则返回 - if (faceInfo.quality.level != seeta::QualityLevel::HIGH) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸质量评估未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 调用算法提取特征值, 1024个字节的float数组 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); - - LOG(DEBUG) << QString("[faceDetect]特征提取成功").toStdString(); - - // 发送信号去进行比对, 执行后续业务逻辑 - emit this->extractFeatureSuccess(CasicFaceRecState::getInstance()); - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; + this->setWorking(false); } + +void FaceDetectRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceDetectRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceDetectRegistProcess::addOneTryCount() +{ + LOG(DEBUG) << QString("[addOneTryCount]已尝试次数[%1]").arg(CasicFaceRecState::getInstance().tryCount).toStdString(); + if (CasicFaceRecState::getInstance().tryCount < SettingConfig::getInstance().MAX_FACE_TRY_COUNT) + { + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + } else + { + CasicFaceRecState::getInstance().tryCount = 0; + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则线程暂停工作 + } +} + +void FaceDetectRegistProcess::addOneNoFaceCount() +{ + CasicFaceRecState::getInstance().noFaceCount++; + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + if (CasicFaceRecState::getInstance().noFaceCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT) + { + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则暂停工作 + } +} + +void FaceDetectRegistProcess::run() +{ + while (exit == false) + { + if (this->working == false) + { + this->msleep(100); // 100ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isFaceQueueEmpty() == true) + { + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(TRACE) << QString("[CasicFaceRegistThread][%1] 人脸图像栈空, 暂停200ms").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + + this->msleep(200); // 200ms后再判断 + continue; + } + + // 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicFaceRecState::getInstance().state != CasicFaceRecState::FaceRecStateName::REC_NOT_START) + { + LOG(DEBUG) << QString("[CasicFaceRegistThread] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicFaceRecState::getInstance().state).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出人脸图像栈中的一条数据 + CasicFaceInfo faceInfo = ProMemory::getInstance().popCasicFace(); + + // 调用人脸检测算法 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceInfo.matData); + + // 没有找到人脸则返回 + if (faceInfo.hasFace == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有多调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 没有找到人脸, 暂停200ms。重置识别状态为REC_NOT_START[%1]").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到人脸则开始一次识别 + if (CasicFaceRecState::getInstance().recoginzeId == "0") + { + CasicFaceRecState::getInstance().initRecognize(); + } + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + CasicFaceRecState::getInstance().tryCount++; // 尝试识别次数+1 + CasicFaceRecState::getInstance().noFaceCount = 0; // 找到脸 则将连续没找到脸的次数清零 + + // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 + casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); + faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); + + // 活体检测判断为假脸 + if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸活体检测未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过活体检测 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_ANTI_SPOOFING; + + // 判定为真实人脸, 开始质量评估 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); + + // 质量评估不为HIGH则返回 + if (faceInfo.quality.level != seeta::QualityLevel::HIGH) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸质量评估未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过质量评估 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_QUALIFY; + + // 调用算法提取特征值, 1024个字节的float数组 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); + + // 设置识别状态值=特征值提取 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_FEATURE_EXTRACT; + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + + // 裁剪识别到的人脸区域 + cv::Rect rect(faceInfo.face.pos.x, faceInfo.face.pos.y, faceInfo.face.pos.width, faceInfo.face.pos.height); + cv::Mat matRect = ImageUtil::MatImageRect(faceInfo.matData, rect, 100); + + QImage tempImg = ImageUtil::MatImageToQImage(matRect); + QByteArray ba; + QBuffer buf(&ba); + tempImg.save(&buf, "bmp"); + CasicFaceRecState::getInstance().imgBase64 = ba.toBase64(); + + LOG(DEBUG) << QString("[CasicFaceRegistThread] 特征提取成功[%1]").arg(CasicFaceRecState::getInstance().toString()).toStdString(); + + this->setWorking(false); + // 特征值提取成功的人脸 与人脸库中的人脸进行比较 + compareFaceInCollection(); + } +} + +void FaceDetectRegistProcess::compareFaceInCollection() +{ + ProMemory::getInstance().clearFaceQueue(); // 特征值提取成功, 开始在库中匹配, 清除人脸图像栈 + QVector faceFeatures = ProMemory::getInstance().getFaceFeatures(); + if (faceFeatures.isEmpty() == true) + { + // 没有人脸特征值数据, 发送信号可以入库 + emit successCaptureFace(""); + } else + { + for (int i = 0; i < faceFeatures.size(); i++) + { + QVariantMap ffMap = faceFeatures.at(i); + + QByteArray ffByteArr = ffMap.value("face_code").toByteArray(); + float ffArr[1024]; + for (int i = 0; i < 1024; i++) + { + QByteArray tempBa; + tempBa.append(ffByteArr.at(i*4)).append(ffByteArr.at(i*4 + 1)).append(ffByteArr.at(i*4 + 2)).append(ffByteArr.at(i*4 + 3)); + ffArr[i] = ByteUtil::binToFloat(tempBa); + } + + float sim = casic::face::CasicFaceInterface::getInstance().faceSimCalculate(CasicFaceRecState::getInstance().faceInfo->feature, ffArr); + + LOG(DEBUG) << QString("[compareFaceInCollection] 特征值比对结果[%1]").arg(sim).toStdString(); + if (sim > 0.62) + { + CasicFaceRecState::getInstance().faceInfo->sim = sim; + CasicFaceRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + CasicFaceRecState::getInstance().recogTimeLast = (float) ((CasicFaceRecState::getInstance().timeStampSucc - CasicFaceRecState::getInstance().timeStamp) / 1000.0); + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_SEARCH_SUCC; + + // 找到匹配的人脸图像 + emit successCaptureFace(ffMap.value("person_id").toString()); + return ; + } + } + + // 没有找到匹配的人脸 + emit successCaptureFace(""); + } +} + diff --git a/device/face/FaceDetectRegistProcess.h b/device/face/FaceDetectRegistProcess.h index 5d42bbe..4df7708 100644 --- a/device/face/FaceDetectRegistProcess.h +++ b/device/face/FaceDetectRegistProcess.h @@ -2,26 +2,48 @@ #define FACEDETECTREGISTPROCESS_H #include +#include #include "opencv2/opencv.hpp" #include "utils/easyloggingpp/easylogging++.h" +#include "utils/ImageUtil.h" +#include "utils/SettingConfig.h" +#include "utils/ByteUtil.h" #include "CasicFaceRecState.h" +#include "casic/ProMemory.h" #include "casic/face/CasicFaceInterface.h" static bool FACE_DETECT_FLAG = false; -class FaceDetectRegistProcess : public QObject +class FaceDetectRegistProcess : public QThread { Q_OBJECT public: explicit FaceDetectRegistProcess(QObject *parent = nullptr); + ~FaceDetectRegistProcess(); -public slots: - void faceDetect(cv::Mat faceMat); + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoFaceCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + void compareFaceInCollection(); + + volatile bool working; + volatile bool exit; signals: - void extractFeatureSuccess(CasicFaceRecState& faceRecState); + void extractFeatureSuccess(); + void failedCaptureFace(); + void successCaptureFace(QString personId); + void findSimFace(QString personId); }; diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp index 7604572..3a4c678 100644 --- a/utils/ImageUtil.cpp +++ b/utils/ImageUtil.cpp @@ -79,7 +79,7 @@ QString base64String = ba.toBase64(); return base64String; } - +*/ cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) { cv::Mat matClone = src.clone(); @@ -94,4 +94,4 @@ return roi; } -*/ + diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/device/face/CasicFaceRecState.cpp b/device/face/CasicFaceRecState.cpp index 3ba8470..7415a86 100644 --- a/device/face/CasicFaceRecState.cpp +++ b/device/face/CasicFaceRecState.cpp @@ -2,5 +2,56 @@ CasicFaceRecState::CasicFaceRecState() { + this->recoginzeId = "0"; + this->timeStamp = -1; + this->state = FaceRecStateName::REC_NOT_START; +} + +void CasicFaceRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noFaceCount = 0; + + LOG(DEBUG) << QString("[CasicFaceRecState][initRecognize] 人脸识别状态初始化").toStdString(); + + // 开始识别 + this->state = FaceRecStateName::REC_DETECT; +} + +QString CasicFaceRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + +QJsonObject CasicFaceRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromLocal8Bit(recoginzeId.data())); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noFaceCount", noFaceCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject faceInfoObj; + faceInfoObj.insert("hasFace", faceInfo->hasFace); + faceInfoObj.insert("reality", faceInfo->antiReality); + faceInfoObj.insert("clarity", faceInfo->antiClarity); + faceInfoObj.insert("sim", faceInfo->sim); + QJsonArray rectArray; + rectArray.append(faceInfo->face.pos.x); + rectArray.append(faceInfo->face.pos.y); + rectArray.append(faceInfo->face.pos.width); + rectArray.append(faceInfo->face.pos.height); + faceInfoObj.insert("faceRect", rectArray); + + obj.insert("faceInfo", faceInfoObj); + + return obj; } diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index 999486e..f259312 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -2,7 +2,13 @@ #define CASICFACERECSTATE_H #include +#include +#include +#include +#include + #include "casic/face/CasicFaceInfo.h" +#include "utils/easyloggingpp/easylogging++.h" class CasicFaceRecState : public QObject { @@ -17,7 +23,7 @@ } void initRecognize(); - std::string toString(); + QString toString(); QJsonObject toJSON(); std::string recoginzeId; // 识别过程id @@ -31,6 +37,19 @@ qint8 noFaceCount = 0; // 连续没有找到人脸次数 float recogTimeLast = 0.0; // 识别成功耗时 + int state; // 识别状态 + + enum FaceRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_DETECT = 1, // 找到人脸 + REC_ANTI_SPOOFING = 2, // 活体检测结束 + REC_QUALIFY = 3, // 质量检测结束 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 人脸库比对成功 + REC_SEARCH_FAIL = 6 // 人脸库比对失败 + }; + private: CasicFaceRecState(); diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp index 82caac5..ba078cc 100644 --- a/device/face/FaceDetectRegistProcess.cpp +++ b/device/face/FaceDetectRegistProcess.cpp @@ -1,72 +1,208 @@ #include "FaceDetectRegistProcess.h" -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QObject(parent) +FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) { - + this->working = false; + this->exit = false; } -void FaceDetectRegistProcess::faceDetect(cv::Mat faceMat) +FaceDetectRegistProcess::~FaceDetectRegistProcess() { - LOG(DEBUG) << "START FACE DETECT" << FACE_DETECT_FLAG; - - // 如果已经在人脸检测工作中则退出 - if (FACE_DETECT_FLAG == true) - { - LOG(DEBUG) << "ALREADY IN FACE DETECT PROCESS"; - return; - } - - // 开始人脸检测 - FACE_DETECT_FLAG = true; - LOG(DEBUG) << "START FACE DETECT"; - - // 调用人脸检测算法 - CasicFaceInfo faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceMat); - if (faceInfo.hasFace == false) - { - // 没有找到人脸进行如下处理 - - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; - return; - } - - // 继续进行后面的步骤 - // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 - casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); - faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); - // 活体检测判断为假脸 - if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸活体检测未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 判定为真实人脸, 开始质量评估 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); - - // 质量评估不为HIGH则返回 - if (faceInfo.quality.level != seeta::QualityLevel::HIGH) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸质量评估未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 调用算法提取特征值, 1024个字节的float数组 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); - - LOG(DEBUG) << QString("[faceDetect]特征提取成功").toStdString(); - - // 发送信号去进行比对, 执行后续业务逻辑 - emit this->extractFeatureSuccess(CasicFaceRecState::getInstance()); - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; + this->setWorking(false); } + +void FaceDetectRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceDetectRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceDetectRegistProcess::addOneTryCount() +{ + LOG(DEBUG) << QString("[addOneTryCount]已尝试次数[%1]").arg(CasicFaceRecState::getInstance().tryCount).toStdString(); + if (CasicFaceRecState::getInstance().tryCount < SettingConfig::getInstance().MAX_FACE_TRY_COUNT) + { + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + } else + { + CasicFaceRecState::getInstance().tryCount = 0; + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则线程暂停工作 + } +} + +void FaceDetectRegistProcess::addOneNoFaceCount() +{ + CasicFaceRecState::getInstance().noFaceCount++; + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + if (CasicFaceRecState::getInstance().noFaceCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT) + { + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则暂停工作 + } +} + +void FaceDetectRegistProcess::run() +{ + while (exit == false) + { + if (this->working == false) + { + this->msleep(100); // 100ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isFaceQueueEmpty() == true) + { + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(TRACE) << QString("[CasicFaceRegistThread][%1] 人脸图像栈空, 暂停200ms").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + + this->msleep(200); // 200ms后再判断 + continue; + } + + // 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicFaceRecState::getInstance().state != CasicFaceRecState::FaceRecStateName::REC_NOT_START) + { + LOG(DEBUG) << QString("[CasicFaceRegistThread] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicFaceRecState::getInstance().state).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出人脸图像栈中的一条数据 + CasicFaceInfo faceInfo = ProMemory::getInstance().popCasicFace(); + + // 调用人脸检测算法 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceInfo.matData); + + // 没有找到人脸则返回 + if (faceInfo.hasFace == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有多调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 没有找到人脸, 暂停200ms。重置识别状态为REC_NOT_START[%1]").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到人脸则开始一次识别 + if (CasicFaceRecState::getInstance().recoginzeId == "0") + { + CasicFaceRecState::getInstance().initRecognize(); + } + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + CasicFaceRecState::getInstance().tryCount++; // 尝试识别次数+1 + CasicFaceRecState::getInstance().noFaceCount = 0; // 找到脸 则将连续没找到脸的次数清零 + + // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 + casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); + faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); + + // 活体检测判断为假脸 + if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸活体检测未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过活体检测 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_ANTI_SPOOFING; + + // 判定为真实人脸, 开始质量评估 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); + + // 质量评估不为HIGH则返回 + if (faceInfo.quality.level != seeta::QualityLevel::HIGH) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸质量评估未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过质量评估 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_QUALIFY; + + // 调用算法提取特征值, 1024个字节的float数组 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); + + // 设置识别状态值=特征值提取 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_FEATURE_EXTRACT; + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + + // 裁剪识别到的人脸区域 + cv::Rect rect(faceInfo.face.pos.x, faceInfo.face.pos.y, faceInfo.face.pos.width, faceInfo.face.pos.height); + cv::Mat matRect = ImageUtil::MatImageRect(faceInfo.matData, rect, 100); + + QImage tempImg = ImageUtil::MatImageToQImage(matRect); + QByteArray ba; + QBuffer buf(&ba); + tempImg.save(&buf, "bmp"); + CasicFaceRecState::getInstance().imgBase64 = ba.toBase64(); + + LOG(DEBUG) << QString("[CasicFaceRegistThread] 特征提取成功[%1]").arg(CasicFaceRecState::getInstance().toString()).toStdString(); + + this->setWorking(false); + // 特征值提取成功的人脸 与人脸库中的人脸进行比较 + compareFaceInCollection(); + } +} + +void FaceDetectRegistProcess::compareFaceInCollection() +{ + ProMemory::getInstance().clearFaceQueue(); // 特征值提取成功, 开始在库中匹配, 清除人脸图像栈 + QVector faceFeatures = ProMemory::getInstance().getFaceFeatures(); + if (faceFeatures.isEmpty() == true) + { + // 没有人脸特征值数据, 发送信号可以入库 + emit successCaptureFace(""); + } else + { + for (int i = 0; i < faceFeatures.size(); i++) + { + QVariantMap ffMap = faceFeatures.at(i); + + QByteArray ffByteArr = ffMap.value("face_code").toByteArray(); + float ffArr[1024]; + for (int i = 0; i < 1024; i++) + { + QByteArray tempBa; + tempBa.append(ffByteArr.at(i*4)).append(ffByteArr.at(i*4 + 1)).append(ffByteArr.at(i*4 + 2)).append(ffByteArr.at(i*4 + 3)); + ffArr[i] = ByteUtil::binToFloat(tempBa); + } + + float sim = casic::face::CasicFaceInterface::getInstance().faceSimCalculate(CasicFaceRecState::getInstance().faceInfo->feature, ffArr); + + LOG(DEBUG) << QString("[compareFaceInCollection] 特征值比对结果[%1]").arg(sim).toStdString(); + if (sim > 0.62) + { + CasicFaceRecState::getInstance().faceInfo->sim = sim; + CasicFaceRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + CasicFaceRecState::getInstance().recogTimeLast = (float) ((CasicFaceRecState::getInstance().timeStampSucc - CasicFaceRecState::getInstance().timeStamp) / 1000.0); + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_SEARCH_SUCC; + + // 找到匹配的人脸图像 + emit successCaptureFace(ffMap.value("person_id").toString()); + return ; + } + } + + // 没有找到匹配的人脸 + emit successCaptureFace(""); + } +} + diff --git a/device/face/FaceDetectRegistProcess.h b/device/face/FaceDetectRegistProcess.h index 5d42bbe..4df7708 100644 --- a/device/face/FaceDetectRegistProcess.h +++ b/device/face/FaceDetectRegistProcess.h @@ -2,26 +2,48 @@ #define FACEDETECTREGISTPROCESS_H #include +#include #include "opencv2/opencv.hpp" #include "utils/easyloggingpp/easylogging++.h" +#include "utils/ImageUtil.h" +#include "utils/SettingConfig.h" +#include "utils/ByteUtil.h" #include "CasicFaceRecState.h" +#include "casic/ProMemory.h" #include "casic/face/CasicFaceInterface.h" static bool FACE_DETECT_FLAG = false; -class FaceDetectRegistProcess : public QObject +class FaceDetectRegistProcess : public QThread { Q_OBJECT public: explicit FaceDetectRegistProcess(QObject *parent = nullptr); + ~FaceDetectRegistProcess(); -public slots: - void faceDetect(cv::Mat faceMat); + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoFaceCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + void compareFaceInCollection(); + + volatile bool working; + volatile bool exit; signals: - void extractFeatureSuccess(CasicFaceRecState& faceRecState); + void extractFeatureSuccess(); + void failedCaptureFace(); + void successCaptureFace(QString personId); + void findSimFace(QString personId); }; diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp index 7604572..3a4c678 100644 --- a/utils/ImageUtil.cpp +++ b/utils/ImageUtil.cpp @@ -79,7 +79,7 @@ QString base64String = ba.toBase64(); return base64String; } - +*/ cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) { cv::Mat matClone = src.clone(); @@ -94,4 +94,4 @@ return roi; } -*/ + diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h index dbfdd48..5ef3c13 100644 --- a/utils/ImageUtil.h +++ b/utils/ImageUtil.h @@ -16,7 +16,7 @@ // static cv::Mat QImageToMat(QImage image); // static QString QImageToBase64(QImage image); -// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); + static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); }; #endif // IMAGEUTIL_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/device/face/CasicFaceRecState.cpp b/device/face/CasicFaceRecState.cpp index 3ba8470..7415a86 100644 --- a/device/face/CasicFaceRecState.cpp +++ b/device/face/CasicFaceRecState.cpp @@ -2,5 +2,56 @@ CasicFaceRecState::CasicFaceRecState() { + this->recoginzeId = "0"; + this->timeStamp = -1; + this->state = FaceRecStateName::REC_NOT_START; +} + +void CasicFaceRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noFaceCount = 0; + + LOG(DEBUG) << QString("[CasicFaceRecState][initRecognize] 人脸识别状态初始化").toStdString(); + + // 开始识别 + this->state = FaceRecStateName::REC_DETECT; +} + +QString CasicFaceRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + +QJsonObject CasicFaceRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromLocal8Bit(recoginzeId.data())); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noFaceCount", noFaceCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject faceInfoObj; + faceInfoObj.insert("hasFace", faceInfo->hasFace); + faceInfoObj.insert("reality", faceInfo->antiReality); + faceInfoObj.insert("clarity", faceInfo->antiClarity); + faceInfoObj.insert("sim", faceInfo->sim); + QJsonArray rectArray; + rectArray.append(faceInfo->face.pos.x); + rectArray.append(faceInfo->face.pos.y); + rectArray.append(faceInfo->face.pos.width); + rectArray.append(faceInfo->face.pos.height); + faceInfoObj.insert("faceRect", rectArray); + + obj.insert("faceInfo", faceInfoObj); + + return obj; } diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index 999486e..f259312 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -2,7 +2,13 @@ #define CASICFACERECSTATE_H #include +#include +#include +#include +#include + #include "casic/face/CasicFaceInfo.h" +#include "utils/easyloggingpp/easylogging++.h" class CasicFaceRecState : public QObject { @@ -17,7 +23,7 @@ } void initRecognize(); - std::string toString(); + QString toString(); QJsonObject toJSON(); std::string recoginzeId; // 识别过程id @@ -31,6 +37,19 @@ qint8 noFaceCount = 0; // 连续没有找到人脸次数 float recogTimeLast = 0.0; // 识别成功耗时 + int state; // 识别状态 + + enum FaceRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_DETECT = 1, // 找到人脸 + REC_ANTI_SPOOFING = 2, // 活体检测结束 + REC_QUALIFY = 3, // 质量检测结束 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 人脸库比对成功 + REC_SEARCH_FAIL = 6 // 人脸库比对失败 + }; + private: CasicFaceRecState(); diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp index 82caac5..ba078cc 100644 --- a/device/face/FaceDetectRegistProcess.cpp +++ b/device/face/FaceDetectRegistProcess.cpp @@ -1,72 +1,208 @@ #include "FaceDetectRegistProcess.h" -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QObject(parent) +FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) { - + this->working = false; + this->exit = false; } -void FaceDetectRegistProcess::faceDetect(cv::Mat faceMat) +FaceDetectRegistProcess::~FaceDetectRegistProcess() { - LOG(DEBUG) << "START FACE DETECT" << FACE_DETECT_FLAG; - - // 如果已经在人脸检测工作中则退出 - if (FACE_DETECT_FLAG == true) - { - LOG(DEBUG) << "ALREADY IN FACE DETECT PROCESS"; - return; - } - - // 开始人脸检测 - FACE_DETECT_FLAG = true; - LOG(DEBUG) << "START FACE DETECT"; - - // 调用人脸检测算法 - CasicFaceInfo faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceMat); - if (faceInfo.hasFace == false) - { - // 没有找到人脸进行如下处理 - - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; - return; - } - - // 继续进行后面的步骤 - // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 - casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); - faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); - // 活体检测判断为假脸 - if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸活体检测未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 判定为真实人脸, 开始质量评估 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); - - // 质量评估不为HIGH则返回 - if (faceInfo.quality.level != seeta::QualityLevel::HIGH) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸质量评估未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 调用算法提取特征值, 1024个字节的float数组 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); - - LOG(DEBUG) << QString("[faceDetect]特征提取成功").toStdString(); - - // 发送信号去进行比对, 执行后续业务逻辑 - emit this->extractFeatureSuccess(CasicFaceRecState::getInstance()); - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; + this->setWorking(false); } + +void FaceDetectRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceDetectRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceDetectRegistProcess::addOneTryCount() +{ + LOG(DEBUG) << QString("[addOneTryCount]已尝试次数[%1]").arg(CasicFaceRecState::getInstance().tryCount).toStdString(); + if (CasicFaceRecState::getInstance().tryCount < SettingConfig::getInstance().MAX_FACE_TRY_COUNT) + { + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + } else + { + CasicFaceRecState::getInstance().tryCount = 0; + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则线程暂停工作 + } +} + +void FaceDetectRegistProcess::addOneNoFaceCount() +{ + CasicFaceRecState::getInstance().noFaceCount++; + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + if (CasicFaceRecState::getInstance().noFaceCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT) + { + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则暂停工作 + } +} + +void FaceDetectRegistProcess::run() +{ + while (exit == false) + { + if (this->working == false) + { + this->msleep(100); // 100ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isFaceQueueEmpty() == true) + { + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(TRACE) << QString("[CasicFaceRegistThread][%1] 人脸图像栈空, 暂停200ms").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + + this->msleep(200); // 200ms后再判断 + continue; + } + + // 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicFaceRecState::getInstance().state != CasicFaceRecState::FaceRecStateName::REC_NOT_START) + { + LOG(DEBUG) << QString("[CasicFaceRegistThread] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicFaceRecState::getInstance().state).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出人脸图像栈中的一条数据 + CasicFaceInfo faceInfo = ProMemory::getInstance().popCasicFace(); + + // 调用人脸检测算法 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceInfo.matData); + + // 没有找到人脸则返回 + if (faceInfo.hasFace == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有多调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 没有找到人脸, 暂停200ms。重置识别状态为REC_NOT_START[%1]").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到人脸则开始一次识别 + if (CasicFaceRecState::getInstance().recoginzeId == "0") + { + CasicFaceRecState::getInstance().initRecognize(); + } + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + CasicFaceRecState::getInstance().tryCount++; // 尝试识别次数+1 + CasicFaceRecState::getInstance().noFaceCount = 0; // 找到脸 则将连续没找到脸的次数清零 + + // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 + casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); + faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); + + // 活体检测判断为假脸 + if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸活体检测未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过活体检测 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_ANTI_SPOOFING; + + // 判定为真实人脸, 开始质量评估 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); + + // 质量评估不为HIGH则返回 + if (faceInfo.quality.level != seeta::QualityLevel::HIGH) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸质量评估未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过质量评估 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_QUALIFY; + + // 调用算法提取特征值, 1024个字节的float数组 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); + + // 设置识别状态值=特征值提取 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_FEATURE_EXTRACT; + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + + // 裁剪识别到的人脸区域 + cv::Rect rect(faceInfo.face.pos.x, faceInfo.face.pos.y, faceInfo.face.pos.width, faceInfo.face.pos.height); + cv::Mat matRect = ImageUtil::MatImageRect(faceInfo.matData, rect, 100); + + QImage tempImg = ImageUtil::MatImageToQImage(matRect); + QByteArray ba; + QBuffer buf(&ba); + tempImg.save(&buf, "bmp"); + CasicFaceRecState::getInstance().imgBase64 = ba.toBase64(); + + LOG(DEBUG) << QString("[CasicFaceRegistThread] 特征提取成功[%1]").arg(CasicFaceRecState::getInstance().toString()).toStdString(); + + this->setWorking(false); + // 特征值提取成功的人脸 与人脸库中的人脸进行比较 + compareFaceInCollection(); + } +} + +void FaceDetectRegistProcess::compareFaceInCollection() +{ + ProMemory::getInstance().clearFaceQueue(); // 特征值提取成功, 开始在库中匹配, 清除人脸图像栈 + QVector faceFeatures = ProMemory::getInstance().getFaceFeatures(); + if (faceFeatures.isEmpty() == true) + { + // 没有人脸特征值数据, 发送信号可以入库 + emit successCaptureFace(""); + } else + { + for (int i = 0; i < faceFeatures.size(); i++) + { + QVariantMap ffMap = faceFeatures.at(i); + + QByteArray ffByteArr = ffMap.value("face_code").toByteArray(); + float ffArr[1024]; + for (int i = 0; i < 1024; i++) + { + QByteArray tempBa; + tempBa.append(ffByteArr.at(i*4)).append(ffByteArr.at(i*4 + 1)).append(ffByteArr.at(i*4 + 2)).append(ffByteArr.at(i*4 + 3)); + ffArr[i] = ByteUtil::binToFloat(tempBa); + } + + float sim = casic::face::CasicFaceInterface::getInstance().faceSimCalculate(CasicFaceRecState::getInstance().faceInfo->feature, ffArr); + + LOG(DEBUG) << QString("[compareFaceInCollection] 特征值比对结果[%1]").arg(sim).toStdString(); + if (sim > 0.62) + { + CasicFaceRecState::getInstance().faceInfo->sim = sim; + CasicFaceRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + CasicFaceRecState::getInstance().recogTimeLast = (float) ((CasicFaceRecState::getInstance().timeStampSucc - CasicFaceRecState::getInstance().timeStamp) / 1000.0); + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_SEARCH_SUCC; + + // 找到匹配的人脸图像 + emit successCaptureFace(ffMap.value("person_id").toString()); + return ; + } + } + + // 没有找到匹配的人脸 + emit successCaptureFace(""); + } +} + diff --git a/device/face/FaceDetectRegistProcess.h b/device/face/FaceDetectRegistProcess.h index 5d42bbe..4df7708 100644 --- a/device/face/FaceDetectRegistProcess.h +++ b/device/face/FaceDetectRegistProcess.h @@ -2,26 +2,48 @@ #define FACEDETECTREGISTPROCESS_H #include +#include #include "opencv2/opencv.hpp" #include "utils/easyloggingpp/easylogging++.h" +#include "utils/ImageUtil.h" +#include "utils/SettingConfig.h" +#include "utils/ByteUtil.h" #include "CasicFaceRecState.h" +#include "casic/ProMemory.h" #include "casic/face/CasicFaceInterface.h" static bool FACE_DETECT_FLAG = false; -class FaceDetectRegistProcess : public QObject +class FaceDetectRegistProcess : public QThread { Q_OBJECT public: explicit FaceDetectRegistProcess(QObject *parent = nullptr); + ~FaceDetectRegistProcess(); -public slots: - void faceDetect(cv::Mat faceMat); + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoFaceCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + void compareFaceInCollection(); + + volatile bool working; + volatile bool exit; signals: - void extractFeatureSuccess(CasicFaceRecState& faceRecState); + void extractFeatureSuccess(); + void failedCaptureFace(); + void successCaptureFace(QString personId); + void findSimFace(QString personId); }; diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp index 7604572..3a4c678 100644 --- a/utils/ImageUtil.cpp +++ b/utils/ImageUtil.cpp @@ -79,7 +79,7 @@ QString base64String = ba.toBase64(); return base64String; } - +*/ cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) { cv::Mat matClone = src.clone(); @@ -94,4 +94,4 @@ return roi; } -*/ + diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h index dbfdd48..5ef3c13 100644 --- a/utils/ImageUtil.h +++ b/utils/ImageUtil.h @@ -16,7 +16,7 @@ // static cv::Mat QImageToMat(QImage image); // static QString QImageToBase64(QImage image); -// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); + static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); }; #endif // IMAGEUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..2cb9b20 --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,18 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/device/face/CasicFaceRecState.cpp b/device/face/CasicFaceRecState.cpp index 3ba8470..7415a86 100644 --- a/device/face/CasicFaceRecState.cpp +++ b/device/face/CasicFaceRecState.cpp @@ -2,5 +2,56 @@ CasicFaceRecState::CasicFaceRecState() { + this->recoginzeId = "0"; + this->timeStamp = -1; + this->state = FaceRecStateName::REC_NOT_START; +} + +void CasicFaceRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noFaceCount = 0; + + LOG(DEBUG) << QString("[CasicFaceRecState][initRecognize] 人脸识别状态初始化").toStdString(); + + // 开始识别 + this->state = FaceRecStateName::REC_DETECT; +} + +QString CasicFaceRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + +QJsonObject CasicFaceRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromLocal8Bit(recoginzeId.data())); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noFaceCount", noFaceCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject faceInfoObj; + faceInfoObj.insert("hasFace", faceInfo->hasFace); + faceInfoObj.insert("reality", faceInfo->antiReality); + faceInfoObj.insert("clarity", faceInfo->antiClarity); + faceInfoObj.insert("sim", faceInfo->sim); + QJsonArray rectArray; + rectArray.append(faceInfo->face.pos.x); + rectArray.append(faceInfo->face.pos.y); + rectArray.append(faceInfo->face.pos.width); + rectArray.append(faceInfo->face.pos.height); + faceInfoObj.insert("faceRect", rectArray); + + obj.insert("faceInfo", faceInfoObj); + + return obj; } diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index 999486e..f259312 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -2,7 +2,13 @@ #define CASICFACERECSTATE_H #include +#include +#include +#include +#include + #include "casic/face/CasicFaceInfo.h" +#include "utils/easyloggingpp/easylogging++.h" class CasicFaceRecState : public QObject { @@ -17,7 +23,7 @@ } void initRecognize(); - std::string toString(); + QString toString(); QJsonObject toJSON(); std::string recoginzeId; // 识别过程id @@ -31,6 +37,19 @@ qint8 noFaceCount = 0; // 连续没有找到人脸次数 float recogTimeLast = 0.0; // 识别成功耗时 + int state; // 识别状态 + + enum FaceRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_DETECT = 1, // 找到人脸 + REC_ANTI_SPOOFING = 2, // 活体检测结束 + REC_QUALIFY = 3, // 质量检测结束 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 人脸库比对成功 + REC_SEARCH_FAIL = 6 // 人脸库比对失败 + }; + private: CasicFaceRecState(); diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp index 82caac5..ba078cc 100644 --- a/device/face/FaceDetectRegistProcess.cpp +++ b/device/face/FaceDetectRegistProcess.cpp @@ -1,72 +1,208 @@ #include "FaceDetectRegistProcess.h" -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QObject(parent) +FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) { - + this->working = false; + this->exit = false; } -void FaceDetectRegistProcess::faceDetect(cv::Mat faceMat) +FaceDetectRegistProcess::~FaceDetectRegistProcess() { - LOG(DEBUG) << "START FACE DETECT" << FACE_DETECT_FLAG; - - // 如果已经在人脸检测工作中则退出 - if (FACE_DETECT_FLAG == true) - { - LOG(DEBUG) << "ALREADY IN FACE DETECT PROCESS"; - return; - } - - // 开始人脸检测 - FACE_DETECT_FLAG = true; - LOG(DEBUG) << "START FACE DETECT"; - - // 调用人脸检测算法 - CasicFaceInfo faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceMat); - if (faceInfo.hasFace == false) - { - // 没有找到人脸进行如下处理 - - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; - return; - } - - // 继续进行后面的步骤 - // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 - casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); - faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); - // 活体检测判断为假脸 - if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸活体检测未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 判定为真实人脸, 开始质量评估 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); - - // 质量评估不为HIGH则返回 - if (faceInfo.quality.level != seeta::QualityLevel::HIGH) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸质量评估未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 调用算法提取特征值, 1024个字节的float数组 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); - - LOG(DEBUG) << QString("[faceDetect]特征提取成功").toStdString(); - - // 发送信号去进行比对, 执行后续业务逻辑 - emit this->extractFeatureSuccess(CasicFaceRecState::getInstance()); - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; + this->setWorking(false); } + +void FaceDetectRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceDetectRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceDetectRegistProcess::addOneTryCount() +{ + LOG(DEBUG) << QString("[addOneTryCount]已尝试次数[%1]").arg(CasicFaceRecState::getInstance().tryCount).toStdString(); + if (CasicFaceRecState::getInstance().tryCount < SettingConfig::getInstance().MAX_FACE_TRY_COUNT) + { + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + } else + { + CasicFaceRecState::getInstance().tryCount = 0; + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则线程暂停工作 + } +} + +void FaceDetectRegistProcess::addOneNoFaceCount() +{ + CasicFaceRecState::getInstance().noFaceCount++; + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + if (CasicFaceRecState::getInstance().noFaceCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT) + { + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则暂停工作 + } +} + +void FaceDetectRegistProcess::run() +{ + while (exit == false) + { + if (this->working == false) + { + this->msleep(100); // 100ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isFaceQueueEmpty() == true) + { + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(TRACE) << QString("[CasicFaceRegistThread][%1] 人脸图像栈空, 暂停200ms").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + + this->msleep(200); // 200ms后再判断 + continue; + } + + // 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicFaceRecState::getInstance().state != CasicFaceRecState::FaceRecStateName::REC_NOT_START) + { + LOG(DEBUG) << QString("[CasicFaceRegistThread] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicFaceRecState::getInstance().state).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出人脸图像栈中的一条数据 + CasicFaceInfo faceInfo = ProMemory::getInstance().popCasicFace(); + + // 调用人脸检测算法 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceInfo.matData); + + // 没有找到人脸则返回 + if (faceInfo.hasFace == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有多调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 没有找到人脸, 暂停200ms。重置识别状态为REC_NOT_START[%1]").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到人脸则开始一次识别 + if (CasicFaceRecState::getInstance().recoginzeId == "0") + { + CasicFaceRecState::getInstance().initRecognize(); + } + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + CasicFaceRecState::getInstance().tryCount++; // 尝试识别次数+1 + CasicFaceRecState::getInstance().noFaceCount = 0; // 找到脸 则将连续没找到脸的次数清零 + + // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 + casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); + faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); + + // 活体检测判断为假脸 + if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸活体检测未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过活体检测 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_ANTI_SPOOFING; + + // 判定为真实人脸, 开始质量评估 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); + + // 质量评估不为HIGH则返回 + if (faceInfo.quality.level != seeta::QualityLevel::HIGH) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸质量评估未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过质量评估 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_QUALIFY; + + // 调用算法提取特征值, 1024个字节的float数组 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); + + // 设置识别状态值=特征值提取 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_FEATURE_EXTRACT; + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + + // 裁剪识别到的人脸区域 + cv::Rect rect(faceInfo.face.pos.x, faceInfo.face.pos.y, faceInfo.face.pos.width, faceInfo.face.pos.height); + cv::Mat matRect = ImageUtil::MatImageRect(faceInfo.matData, rect, 100); + + QImage tempImg = ImageUtil::MatImageToQImage(matRect); + QByteArray ba; + QBuffer buf(&ba); + tempImg.save(&buf, "bmp"); + CasicFaceRecState::getInstance().imgBase64 = ba.toBase64(); + + LOG(DEBUG) << QString("[CasicFaceRegistThread] 特征提取成功[%1]").arg(CasicFaceRecState::getInstance().toString()).toStdString(); + + this->setWorking(false); + // 特征值提取成功的人脸 与人脸库中的人脸进行比较 + compareFaceInCollection(); + } +} + +void FaceDetectRegistProcess::compareFaceInCollection() +{ + ProMemory::getInstance().clearFaceQueue(); // 特征值提取成功, 开始在库中匹配, 清除人脸图像栈 + QVector faceFeatures = ProMemory::getInstance().getFaceFeatures(); + if (faceFeatures.isEmpty() == true) + { + // 没有人脸特征值数据, 发送信号可以入库 + emit successCaptureFace(""); + } else + { + for (int i = 0; i < faceFeatures.size(); i++) + { + QVariantMap ffMap = faceFeatures.at(i); + + QByteArray ffByteArr = ffMap.value("face_code").toByteArray(); + float ffArr[1024]; + for (int i = 0; i < 1024; i++) + { + QByteArray tempBa; + tempBa.append(ffByteArr.at(i*4)).append(ffByteArr.at(i*4 + 1)).append(ffByteArr.at(i*4 + 2)).append(ffByteArr.at(i*4 + 3)); + ffArr[i] = ByteUtil::binToFloat(tempBa); + } + + float sim = casic::face::CasicFaceInterface::getInstance().faceSimCalculate(CasicFaceRecState::getInstance().faceInfo->feature, ffArr); + + LOG(DEBUG) << QString("[compareFaceInCollection] 特征值比对结果[%1]").arg(sim).toStdString(); + if (sim > 0.62) + { + CasicFaceRecState::getInstance().faceInfo->sim = sim; + CasicFaceRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + CasicFaceRecState::getInstance().recogTimeLast = (float) ((CasicFaceRecState::getInstance().timeStampSucc - CasicFaceRecState::getInstance().timeStamp) / 1000.0); + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_SEARCH_SUCC; + + // 找到匹配的人脸图像 + emit successCaptureFace(ffMap.value("person_id").toString()); + return ; + } + } + + // 没有找到匹配的人脸 + emit successCaptureFace(""); + } +} + diff --git a/device/face/FaceDetectRegistProcess.h b/device/face/FaceDetectRegistProcess.h index 5d42bbe..4df7708 100644 --- a/device/face/FaceDetectRegistProcess.h +++ b/device/face/FaceDetectRegistProcess.h @@ -2,26 +2,48 @@ #define FACEDETECTREGISTPROCESS_H #include +#include #include "opencv2/opencv.hpp" #include "utils/easyloggingpp/easylogging++.h" +#include "utils/ImageUtil.h" +#include "utils/SettingConfig.h" +#include "utils/ByteUtil.h" #include "CasicFaceRecState.h" +#include "casic/ProMemory.h" #include "casic/face/CasicFaceInterface.h" static bool FACE_DETECT_FLAG = false; -class FaceDetectRegistProcess : public QObject +class FaceDetectRegistProcess : public QThread { Q_OBJECT public: explicit FaceDetectRegistProcess(QObject *parent = nullptr); + ~FaceDetectRegistProcess(); -public slots: - void faceDetect(cv::Mat faceMat); + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoFaceCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + void compareFaceInCollection(); + + volatile bool working; + volatile bool exit; signals: - void extractFeatureSuccess(CasicFaceRecState& faceRecState); + void extractFeatureSuccess(); + void failedCaptureFace(); + void successCaptureFace(QString personId); + void findSimFace(QString personId); }; diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp index 7604572..3a4c678 100644 --- a/utils/ImageUtil.cpp +++ b/utils/ImageUtil.cpp @@ -79,7 +79,7 @@ QString base64String = ba.toBase64(); return base64String; } - +*/ cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) { cv::Mat matClone = src.clone(); @@ -94,4 +94,4 @@ return roi; } -*/ + diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h index dbfdd48..5ef3c13 100644 --- a/utils/ImageUtil.h +++ b/utils/ImageUtil.h @@ -16,7 +16,7 @@ // static cv::Mat QImageToMat(QImage image); // static QString QImageToBase64(QImage image); -// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); + static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); }; #endif // IMAGEUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..2cb9b20 --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,18 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} diff --git a/utils/SpeakerUtil.h b/utils/SpeakerUtil.h new file mode 100644 index 0000000..d4c1074 --- /dev/null +++ b/utils/SpeakerUtil.h @@ -0,0 +1,31 @@ +#ifndef SPEAKERUTIL_H +#define SPEAKERUTIL_H + +#include +#include + +class SpeakerUtil : public QObject +{ + Q_OBJECT +public: + ~SpeakerUtil() {}; + SpeakerUtil(const SpeakerUtil&)=delete; + SpeakerUtil& operator=(const SpeakerUtil&)=delete; + + static SpeakerUtil& getInstance() { + static SpeakerUtil instance; + return instance; + } + + void speak(QString content); + +private: + explicit SpeakerUtil(QObject *parent = nullptr); + + QTextToSpeech * tts; + +signals: + +}; + +#endif // SPEAKERUTIL_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 55cbba0..31bda26 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -162,6 +162,43 @@ } } +void AddPersonForm::onFailedCaptureFace() +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("没有找到人脸,请重试")); +} + +void AddPersonForm::onSuccessCaptureFace(QString personId) +{ + faceLabel->hide(); + ProMemory::getInstance().faceCam->stopTakingPhoto(); + SpeakerUtil::getInstance().speak(QString("人脸采集成功")); + LOG(DEBUG) << "人脸采集成功"; + if (personId == "") + { + // 人脸库中没有 + // 设置显示人脸照片 + face = CasicFaceRecState::getInstance().imgBase64; + QPixmap pm; + pm.loadFromData(QByteArray::fromBase64(face.toLocal8Bit())); + pm = pm.scaledToHeight(ui->labPhotoFace->height(), Qt::SmoothTransformation); + ui->labPhotoFace->setPixmap(pm); + + // 计算特征值的字节数组 + for (int i = 0; i < 1024; i++) + { + faceCode.append(ByteUtil::floatToBytes(CasicFaceRecState::getInstance().faceInfo->feature[i])); + } + } else { + loadPersonInfo(personId); + + LOG(DEBUG) << "人脸已经注册" << ui->inputName->text().toStdString(); + + ui->btnSave->setDisabled(true); + } +} + bool AddPersonForm::validateForm() { @@ -269,12 +306,18 @@ void AddPersonForm::onPhotoFaceDoubleClicked() { + ProMemory::getInstance().faceCam->openFaceCamera(); + // 1人脸图像显示的容器 faceLabel->resize(1280, 720); faceLabel->move(0, 80); faceLabel->raise(); faceLabel->show(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE; + ProMemory::getInstance().faceRegistPro->setWorking(true); + CasicFaceRecState::getInstance().state = CasicFaceRecState::REC_NOT_START; + LOG(TRACE) << "FACE IMAGE DOUBLE CLICKED"; } diff --git a/AddPersonForm.h b/AddPersonForm.h index e9cc7f3..ae9a154 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -9,6 +9,7 @@ #include "dao/util/CacheManager.h" #include "utils/SelectDeptUtil.h" #include "utils/SettingConfig.h" +#include "utils/SpeakerUtil.h" #include "utils/easyloggingpp/easylogging++.h" namespace Ui { @@ -31,6 +32,8 @@ public slots: void drawImageOnForm(QImage imageDisp); + void onFailedCaptureFace(); + void onSuccessCaptureFace(QString personId); private slots: void on_btnBack_clicked(); @@ -47,6 +50,9 @@ QString personId; + QString face; // 人脸图像数据 + QByteArray faceCode; // 人脸特征编码数据 + QLabel * faceLabel; // 采集人脸时显示的画面 bool validateForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 41f36a4..cd4a9bc 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -18,7 +18,7 @@ include(utils/utils.pri) include(dao/dao.pri) include(device/device.pri) -include(casic/face/casicFace.pri) +include(casic/casic.pri) SOURCES += main.cpp SOURCES += CasicBioRecWin.cpp diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index 1b9e454..2179273 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -22,25 +22,29 @@ qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); // 初始化各个form界面并绑定切换响应函数 - this->initFormsPtr(); + initFormsPtr(); + + // 初始化识别和注册的各个线程 + initFaceRegistThread(); // 人脸注册线程 // 初始化人脸相机控制 - this->faceCam = new FaceCameraController(this); - connect(faceCam, &FaceCameraController::sendImageToDraw, + ProMemory::getInstance().faceCam = new FaceCameraController(this); + connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDraw, addPersonForm, &AddPersonForm::drawImageOnForm); - faceCam->openFaceCamera(); - - this->faceRegistPro = new FaceDetectRegistProcess(this); - connect(faceCam, &FaceCameraController::sendImageToDetect, - faceRegistPro, &FaceDetectRegistProcess::faceDetect); - // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } CasicBioRecWin::~CasicBioRecWin() { + ProMemory::getInstance().faceRegistPro->exitThread(); + ProMemory::getInstance().faceRegistPro->deleteLater(); + ProMemory::getInstance().faceRegistPro->wait(); + + delete ProMemory::getInstance().faceRegistPro; + delete ui; } @@ -55,23 +59,20 @@ } } -AddPersonForm * CasicBioRecWin::getAddPersonFormPtr() -{ - return addPersonForm; -} - void CasicBioRecWin::backToStandByForm() { ui->stacked->setCurrentWidget(startForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; } void CasicBioRecWin::switchToUserListForm() { personListForm->findPersonList(); ui->stacked->setCurrentWidget(personListForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::PERSON_LIST_FORM; } void CasicBioRecWin::switchToSettingForm() { - + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::SETTING_FORM; } void CasicBioRecWin::switchToRegistForm(QString personId) { @@ -92,6 +93,7 @@ // 切换页面 ui->stacked->setCurrentWidget(addPersonForm); + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_FORM; } void CasicBioRecWin::initFormsPtr() @@ -106,7 +108,6 @@ ui->stacked->addWidget(settingForm); ui->stacked->addWidget(addPersonForm); -// ui->stacked->setCurrentWidget(personListForm); // 绑定按钮函数 connect(startForm, &StartupForm::switchToUserListForm, @@ -129,3 +130,20 @@ { CacheManager::getInstance().updateDeptCache(); } + +void CasicBioRecWin::initFaceRegistThread() +{ + // 人脸注册处理过程 + ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + + // 绑定信号与槽函数 + // 采集人脸图像失败 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + addPersonForm, &AddPersonForm::onFailedCaptureFace); + + // 采集人脸图像成功 + connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + addPersonForm, &AddPersonForm::onSuccessCaptureFace); + + ProMemory::getInstance().faceRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index a10397f..3e706fe 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -3,6 +3,7 @@ #include +#include "casic/ProMemory.h" #include "dao/util/CacheManager.h" #include "utils/SettingConfig.h" #include "utils/easyloggingpp/easylogging++.h" @@ -27,11 +28,6 @@ CasicBioRecWin(QWidget *parent = nullptr); ~CasicBioRecWin(); - FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; - - AddPersonForm * getAddPersonFormPtr(); - public slots: void backToStandByForm(); void switchToUserListForm(); @@ -49,6 +45,7 @@ void keyPressEvent(QKeyEvent *event); void initFormsPtr(); void initCacheData(); + void initFaceRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/casic/CasicBioRecConst.h b/casic/CasicBioRecConst.h new file mode 100644 index 0000000..f55c84f --- /dev/null +++ b/casic/CasicBioRecConst.h @@ -0,0 +1,19 @@ +#ifndef CASICBIORECCONST_H +#define CASICBIORECCONST_H + +class CasicBioRecConst +{ +public: + enum WidgeFrameName + { + MAIN_PAGE = 0, // 主页面 + PERSON_LIST_FORM = 1, // 人员列表页面 + ADD_PERSON_FORM = 2, // 添加人员页面 + ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 + ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 + SETTING_FORM = 3, // 设置页面 + RECOGNIZE_SUCCESS_FORM = 4 // 识别成功界面 + }; +}; + +#endif // CASICBIORECCONST_H diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp new file mode 100644 index 0000000..789bf94 --- /dev/null +++ b/casic/ProMemory.cpp @@ -0,0 +1,162 @@ +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + this->initFaceFeatures(); +} + +ProMemory::~ProMemory() +{ + +} + +QVector ProMemory::getFaceFeatures() +{ + return this->faceFeatures; +} + +void ProMemory::pushCasicFace(CasicFaceInfo faceInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 30) + { + QStack empty; + std::swap(empty, faceQueue); + } + + faceQueue.push(faceInfo); + mutex.unlock(); +} + +CasicFaceInfo ProMemory::popCasicFace() +{ + CasicFaceInfo result; + + QMutex mutex; + mutex.lock(); + if (this->faceQueue.size() > 0) + { + result = faceQueue.pop(); + } + + mutex.unlock(); + + return result; +} + +int ProMemory::getFaceQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + + size = this->faceQueue.size(); + + mutex.unlock(); + + return size; +} + +bool ProMemory::isFaceQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + + empty = this->faceQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} + +void ProMemory::clearFaceQueue() +{ + QMutex mutex; + mutex.lock(); + + QStack empty; + std::swap(empty, faceQueue); + + mutex.unlock(); +} + + +//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +//{ +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 30) +// { +// QStack empty; +// swap(empty, irisQueue); +// } + +// irisQueue.push(irisInfo); +// mutex.unlock(); +//} +//CasicIrisInfo ProMemory::popCasicIris() +//{ +// CasicIrisInfo result; + +// QMutex mutex; +// mutex.lock(); +// if (this->irisQueue.size() > 0) +// { +// result = irisQueue.pop(); +// } + +// mutex.unlock(); + +// return result; +//} +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + QMutex mutex; + mutex.lock(); + +// size = this->irisQueue.size(); + + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + QMutex mutex; + mutex.lock(); + +// empty = this->irisQueue.isEmpty(); + + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + QMutex mutex; + mutex.lock(); + +// QStack empty; +// swap(empty, irisQueue); + + mutex.unlock(); +} + +void ProMemory::initFaceFeatures() +{ + FaceDataDao faceDataDao; + this->faceFeatures = faceDataDao.findAllRecord(); +} + +void ProMemory::initIrisFeatures() +{ + +} diff --git a/casic/ProMemory.h b/casic/ProMemory.h new file mode 100644 index 0000000..33010d2 --- /dev/null +++ b/casic/ProMemory.h @@ -0,0 +1,66 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "CasicBioRecConst.h" +#include "casic/face/CasicFaceInfo.h" +//#include "casic/iris/CasicIrisInfo.h" +#include "dao/FaceDataDao.h" +#include "dao/IrisDataDao.h" + +#include "device/FaceCameraController.h" +#include "device/face/FaceDetectRegistProcess.h" + +class FaceCameraController; +class FaceDetectRegistProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicFace(CasicFaceInfo faceInfo); + CasicFaceInfo popCasicFace(); + + int getFaceQueueSize(); + bool isFaceQueueEmpty(); + void clearFaceQueue(); + +// void pushCasicIris(CasicIrisInfo irisInfo); +// CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame; + QString capFaceOrIris; // 注册虹膜或者人脸 + + void initFaceFeatures(); // 初始化人脸特征值集合 + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getFaceFeatures(); + + FaceCameraController * faceCam; + FaceDetectRegistProcess * faceRegistPro; +private: + ProMemory(); + + QStack faceQueue; // 人脸信息队列 +// QStack irisQueue; // 虹膜信息队列 +// QStack irisLeftQueue; // 左眼信息队列 +// QStack irisRightQueue; // 右眼信息队列 + + QVector faceFeatures; // 人脸特征值集合 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..852152f --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,5 @@ +include(face/casicFace.pri) + +HEADERS += $$PWD/CasicBioRecConst.h +HEADERS += $$PWD/ProMemory.h +SOURCES += $$PWD/ProMemory.cpp diff --git a/casic/face/CasicFaceInterface.cpp b/casic/face/CasicFaceInterface.cpp index 6afa140..5e09b86 100644 --- a/casic/face/CasicFaceInterface.cpp +++ b/casic/face/CasicFaceInterface.cpp @@ -126,7 +126,7 @@ LOG(DEBUG) << QString("人脸检测算法[tm: %1 ms][count: %2][rect: (%3,%4), (%5,%6)][size: (%7,%8)]") .arg(timer.elapsed()).arg(faces.size) .arg(faces.data[0].pos.x).arg(faces.data[0].pos.y).arg(faces.data[0].pos.x + faces.data[0].pos.width).arg(faces.data[0].pos.y + faces.data[0].pos.height) - .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toLocal8Bit().data(); + .arg(faces.data[0].pos.width).arg(faces.data[0].pos.height).toStdString(); } CasicFaceInfo faceInfo; @@ -168,7 +168,7 @@ seeta::QualityOfBrightness qBright; seeta::QualityResult brightResult = qBright.check(image, face, points, 5); - LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toLocal8Bit().data(); + LOG(DEBUG) << QString("亮度评估[tm: %1 ms][bright: %2][score: %3]").arg(timer.elapsed()).arg(brightResult.level).arg(brightResult.score).toStdString(); if (brightResult.level != seeta::QualityLevel::HIGH) { @@ -183,7 +183,7 @@ seeta::QualityOfClarity qClarity; seeta::QualityResult clarityResult = qClarity.check(image, face, points, 5); - LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("清晰度评估[tm: %1 ms][clarity: %2]").arg(timer.elapsed()).arg(clarityResult.level).toStdString(); if (clarityResult.level != seeta::QualityLevel::HIGH) { @@ -213,7 +213,7 @@ // 分辨率评估 seeta::QualityOfResolution qReso; seeta::QualityResult resoResult = qReso.check(image, face, points, 5); - LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("分辨率评估[tm: %1 ms][reso: %2]").arg(timer.elapsed()).arg(resoResult.level).toStdString(); if (resoResult.level != seeta::QualityLevel::HIGH) { // 分辨率不够, 直接返回 @@ -245,7 +245,7 @@ seeta::QualityResult poseResult = poseEx->check(image, face, points, 5); - LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toLocal8Bit().data(); + LOG(DEBUG) << QString("姿势评估[tm: %1ms][pose: %2][score: %3]").arg(timer.elapsed()).arg(poseResult.score).arg(poseResult.level).toStdString(); if (poseResult.level != seeta::QualityLevel::HIGH) { @@ -291,7 +291,7 @@ processor->GetPreFrameScore(&faceInfo.antiClarity, &faceInfo.antiReality); - LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toLocal8Bit().data(); + LOG(DEBUG) << QString("活体检测[tm: %1 ms][anti: %2][clarity: %3, reality: %4]").arg(timer.elapsed()).arg(status).arg(faceInfo.antiClarity).arg(faceInfo.antiReality).toStdString(); } return faceInfo; @@ -363,11 +363,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(TRACE) << QString("人脸分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } @@ -393,11 +393,11 @@ if (rect.size() == 0) { - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][0]").arg(timer.elapsed()).toStdString(); return cv::Rect(0, 0, 0, 0); } - LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toLocal8Bit().data(); + LOG(DEBUG) << QString("眼睛分类检测算法[tm: %1 ms][%2, %3]").arg(timer.elapsed()).arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); return rect.at(0); } diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 642f3fb..89a05da 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -30,7 +30,7 @@ result.append(item); } - LOG(DEBUG) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toLocal8Bit().data(); + LOG(TRACE) << QString("查询FACE_DATA表的所有记录[%1]").arg(result.size()).toStdString(); return result; } diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5a0420c..fd9cb55 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -6,9 +6,7 @@ FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { - // 获取定时器, 绑定定时函数 - connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, - this, &FaceCameraController::getOneFaceFrm); + } FaceCameraController::~FaceCameraController() @@ -19,6 +17,10 @@ void FaceCameraController::openFaceCamera() { + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); + this->faceCap = new cv::VideoCapture(SettingConfig::getInstance().FACE_CAMERA_INDEX, cv::CAP_DSHOW); faceCap->set(cv::CAP_PROP_FRAME_WIDTH, SettingConfig::getInstance().FACE_FRAME_WIDTH); faceCap->set(cv::CAP_PROP_FRAME_HEIGHT, SettingConfig::getInstance().FACE_FRAME_HEIGHT); @@ -29,13 +31,17 @@ .arg(SettingConfig::getInstance().FACE_FRAME_HEIGHT).toStdString(); // 启动定时器 -// TimeCounterUtil::getInstance().faceCapCounter->setInterval(SettingConfig::getInstance().FACE_FRAME_INTERVAL); -// TimeCounterUtil::getInstance().faceCapCounter->start(); TimeCounterUtil::getInstance().faceCapCounter->start(SettingConfig::getInstance().FACE_FRAME_INTERVAL); LOG(DEBUG) << QString("[FaceCameraController][openFaceCamera]相机开始拍图[%1ms]") .arg(SettingConfig::getInstance().FACE_FRAME_INTERVAL).toStdString(); } +void FaceCameraController::stopTakingPhoto() +{ + disconnect(TimeCounterUtil::getInstance().faceCapCounter, &QTimer::timeout, + this, &FaceCameraController::getOneFaceFrm); +} + void FaceCameraController::closeFaceCamera() { faceCap->release(); @@ -46,26 +52,40 @@ void FaceCameraController::getOneFaceFrm() { - faceCap->read(faceMat); - - // clone一个mat, 用于界面显示 - cv::Mat faceMatDisp = faceMat.clone(); - - cv::Mat imageMatMiir; - flip(faceMatDisp, imageMatMiir, 1); // 左右翻转 - QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); - - CasicBioRecWin * rootPtr = (CasicBioRecWin *) this->parent(); - if (rootPtr->getAddPersonFormPtr()->isVisible() == 1) + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_FACE) { - rootPtr->getAddPersonFormPtr()->drawImageOnForm(imgDisplay); + // 拍图 + faceCap->read(faceMat); + + LOG(TRACE) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; + + // clone一个mat, 用于界面显示 + cv::Mat faceMatDisp = faceMat.clone(); + + // 左右翻转 + cv::Mat imageMatMiir; + flip(faceMatDisp, imageMatMiir, 1); + + // 利用人脸分类器进行初步的检测 + cv::Rect faceRect = casic::face::CasicFaceInterface::getInstance().faceDetectByCVCascade(imageMatMiir); + if (faceRect.width > 0) + { + // 初筛检测到人脸时将图像入栈 + CasicFaceInfo faceInfo; + faceInfo.hasFace = false; + faceInfo.matData = faceMat; + + // 将图片数据压入堆栈 + ProMemory::getInstance().pushCasicFace(faceInfo); + + // 检测到人脸则绘制一个绿色的边框 + rectangle(imageMatMiir, faceRect, cv::Scalar(0, 255, 0), 2); + } + + // 将mat转成QImage + QImage imgDisplay = ImageUtil::MatImageToQImage(imageMatMiir); + + // 发送信号用于界面显示 + emit sendImageToDraw(imgDisplay); } - - // 发送信号用于界面显示 -// emit sendImageToDraw(faceMatDisp); - - LOG(DEBUG) << "TAKE ONE FACE FRAME " << faceMat.cols << " * " << faceMat.rows; - - // 发送信号用于人脸检测和生成特征值 - emit sendImageToDetect(faceMat); } diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index fb38bf6..6570cfb 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -5,6 +5,7 @@ #include "opencv2/opencv.hpp" +#include "casic/ProMemory.h" //#include "casic/face/CasicFaceInterface.h" //#include "process/memory/ProMemory.h" //#include "process/face/CasicFaceRecState.h" @@ -22,6 +23,7 @@ // 初始化并打开人脸相机 void openFaceCamera(); + void stopTakingPhoto(); void closeFaceCamera(); private: diff --git a/device/face/CasicFaceRecState.cpp b/device/face/CasicFaceRecState.cpp index 3ba8470..7415a86 100644 --- a/device/face/CasicFaceRecState.cpp +++ b/device/face/CasicFaceRecState.cpp @@ -2,5 +2,56 @@ CasicFaceRecState::CasicFaceRecState() { + this->recoginzeId = "0"; + this->timeStamp = -1; + this->state = FaceRecStateName::REC_NOT_START; +} + +void CasicFaceRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noFaceCount = 0; + + LOG(DEBUG) << QString("[CasicFaceRecState][initRecognize] 人脸识别状态初始化").toStdString(); + + // 开始识别 + this->state = FaceRecStateName::REC_DETECT; +} + +QString CasicFaceRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + +QJsonObject CasicFaceRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromLocal8Bit(recoginzeId.data())); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noFaceCount", noFaceCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject faceInfoObj; + faceInfoObj.insert("hasFace", faceInfo->hasFace); + faceInfoObj.insert("reality", faceInfo->antiReality); + faceInfoObj.insert("clarity", faceInfo->antiClarity); + faceInfoObj.insert("sim", faceInfo->sim); + QJsonArray rectArray; + rectArray.append(faceInfo->face.pos.x); + rectArray.append(faceInfo->face.pos.y); + rectArray.append(faceInfo->face.pos.width); + rectArray.append(faceInfo->face.pos.height); + faceInfoObj.insert("faceRect", rectArray); + + obj.insert("faceInfo", faceInfoObj); + + return obj; } diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index 999486e..f259312 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -2,7 +2,13 @@ #define CASICFACERECSTATE_H #include +#include +#include +#include +#include + #include "casic/face/CasicFaceInfo.h" +#include "utils/easyloggingpp/easylogging++.h" class CasicFaceRecState : public QObject { @@ -17,7 +23,7 @@ } void initRecognize(); - std::string toString(); + QString toString(); QJsonObject toJSON(); std::string recoginzeId; // 识别过程id @@ -31,6 +37,19 @@ qint8 noFaceCount = 0; // 连续没有找到人脸次数 float recogTimeLast = 0.0; // 识别成功耗时 + int state; // 识别状态 + + enum FaceRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_DETECT = 1, // 找到人脸 + REC_ANTI_SPOOFING = 2, // 活体检测结束 + REC_QUALIFY = 3, // 质量检测结束 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 人脸库比对成功 + REC_SEARCH_FAIL = 6 // 人脸库比对失败 + }; + private: CasicFaceRecState(); diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp index 82caac5..ba078cc 100644 --- a/device/face/FaceDetectRegistProcess.cpp +++ b/device/face/FaceDetectRegistProcess.cpp @@ -1,72 +1,208 @@ #include "FaceDetectRegistProcess.h" -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QObject(parent) +FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) { - + this->working = false; + this->exit = false; } -void FaceDetectRegistProcess::faceDetect(cv::Mat faceMat) +FaceDetectRegistProcess::~FaceDetectRegistProcess() { - LOG(DEBUG) << "START FACE DETECT" << FACE_DETECT_FLAG; - - // 如果已经在人脸检测工作中则退出 - if (FACE_DETECT_FLAG == true) - { - LOG(DEBUG) << "ALREADY IN FACE DETECT PROCESS"; - return; - } - - // 开始人脸检测 - FACE_DETECT_FLAG = true; - LOG(DEBUG) << "START FACE DETECT"; - - // 调用人脸检测算法 - CasicFaceInfo faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceMat); - if (faceInfo.hasFace == false) - { - // 没有找到人脸进行如下处理 - - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; - return; - } - - // 继续进行后面的步骤 - // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 - casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); - faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); - // 活体检测判断为假脸 - if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸活体检测未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 判定为真实人脸, 开始质量评估 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); - - // 质量评估不为HIGH则返回 - if (faceInfo.quality.level != seeta::QualityLevel::HIGH) - { - // 表示本次识别结束, 可以开始下一次识别过程 - LOG(DEBUG) << QString("[faceDetect]人脸质量评估未通过").toStdString(); - - FACE_DETECT_FLAG = false; - return; - } - - // 调用算法提取特征值, 1024个字节的float数组 - faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); - - LOG(DEBUG) << QString("[faceDetect]特征提取成功").toStdString(); - - // 发送信号去进行比对, 执行后续业务逻辑 - emit this->extractFeatureSuccess(CasicFaceRecState::getInstance()); - - // 结束检测 重置工作标志位 - FACE_DETECT_FLAG = false; + this->setWorking(false); } + +void FaceDetectRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceDetectRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceDetectRegistProcess::addOneTryCount() +{ + LOG(DEBUG) << QString("[addOneTryCount]已尝试次数[%1]").arg(CasicFaceRecState::getInstance().tryCount).toStdString(); + if (CasicFaceRecState::getInstance().tryCount < SettingConfig::getInstance().MAX_FACE_TRY_COUNT) + { + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + } else + { + CasicFaceRecState::getInstance().tryCount = 0; + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则线程暂停工作 + } +} + +void FaceDetectRegistProcess::addOneNoFaceCount() +{ + CasicFaceRecState::getInstance().noFaceCount++; + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_NOT_START; + if (CasicFaceRecState::getInstance().noFaceCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT) + { + CasicFaceRecState::getInstance().noFaceCount = 0; + emit failedCaptureFace(); + + this->setWorking(false); // 返回后则暂停工作 + } +} + +void FaceDetectRegistProcess::run() +{ + while (exit == false) + { + if (this->working == false) + { + this->msleep(100); // 100ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isFaceQueueEmpty() == true) + { + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(TRACE) << QString("[CasicFaceRegistThread][%1] 人脸图像栈空, 暂停200ms").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + + this->msleep(200); // 200ms后再判断 + continue; + } + + // 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicFaceRecState::getInstance().state != CasicFaceRecState::FaceRecStateName::REC_NOT_START) + { + LOG(DEBUG) << QString("[CasicFaceRegistThread] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicFaceRecState::getInstance().state).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出人脸图像栈中的一条数据 + CasicFaceInfo faceInfo = ProMemory::getInstance().popCasicFace(); + + // 调用人脸检测算法 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceDetect(faceInfo.matData); + + // 没有找到人脸则返回 + if (faceInfo.hasFace == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + addOneNoFaceCount(); // 连续没找到脸的次数+1;注册过程有多调用opencv的分类器, 没有符合要求脸的图像不入栈 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 没有找到人脸, 暂停200ms。重置识别状态为REC_NOT_START[%1]").arg(CasicFaceRecState::getInstance().noFaceCount).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到人脸则开始一次识别 + if (CasicFaceRecState::getInstance().recoginzeId == "0") + { + CasicFaceRecState::getInstance().initRecognize(); + } + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + CasicFaceRecState::getInstance().tryCount++; // 尝试识别次数+1 + CasicFaceRecState::getInstance().noFaceCount = 0; // 找到脸 则将连续没找到脸的次数清零 + + // 开始人脸活体检测, 提高活体检测的阈值到0.3/0.6 + casic::face::CasicFaceInterface::getInstance().setAntiThreshold(0.3, 0.6); + faceInfo = casic::face::CasicFaceInterface::getInstance().faceAntiSpoofing(faceInfo); + + // 活体检测判断为假脸 + if (faceInfo.antiStatus != seeta::FaceAntiSpoofing::Status::REAL) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸活体检测未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过活体检测 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_ANTI_SPOOFING; + + // 判定为真实人脸, 开始质量评估 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceQuality(faceInfo); + + // 质量评估不为HIGH则返回 + if (faceInfo.quality.level != seeta::QualityLevel::HIGH) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicFaceRegistThread] 人脸质量评估未通过, 暂停200ms。[%1]").arg("CasicFaceRecState::getInstance().toString()").toStdString(); + + addOneTryCount(); // 判断是否超过次数, 重置识别状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 设置识别状态值=通过质量评估 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_QUALIFY; + + // 调用算法提取特征值, 1024个字节的float数组 + faceInfo = casic::face::CasicFaceInterface::getInstance().faceFeatureExtract(faceInfo); + + // 设置识别状态值=特征值提取 + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_FEATURE_EXTRACT; + CasicFaceRecState::getInstance().faceInfo = &faceInfo; + + // 裁剪识别到的人脸区域 + cv::Rect rect(faceInfo.face.pos.x, faceInfo.face.pos.y, faceInfo.face.pos.width, faceInfo.face.pos.height); + cv::Mat matRect = ImageUtil::MatImageRect(faceInfo.matData, rect, 100); + + QImage tempImg = ImageUtil::MatImageToQImage(matRect); + QByteArray ba; + QBuffer buf(&ba); + tempImg.save(&buf, "bmp"); + CasicFaceRecState::getInstance().imgBase64 = ba.toBase64(); + + LOG(DEBUG) << QString("[CasicFaceRegistThread] 特征提取成功[%1]").arg(CasicFaceRecState::getInstance().toString()).toStdString(); + + this->setWorking(false); + // 特征值提取成功的人脸 与人脸库中的人脸进行比较 + compareFaceInCollection(); + } +} + +void FaceDetectRegistProcess::compareFaceInCollection() +{ + ProMemory::getInstance().clearFaceQueue(); // 特征值提取成功, 开始在库中匹配, 清除人脸图像栈 + QVector faceFeatures = ProMemory::getInstance().getFaceFeatures(); + if (faceFeatures.isEmpty() == true) + { + // 没有人脸特征值数据, 发送信号可以入库 + emit successCaptureFace(""); + } else + { + for (int i = 0; i < faceFeatures.size(); i++) + { + QVariantMap ffMap = faceFeatures.at(i); + + QByteArray ffByteArr = ffMap.value("face_code").toByteArray(); + float ffArr[1024]; + for (int i = 0; i < 1024; i++) + { + QByteArray tempBa; + tempBa.append(ffByteArr.at(i*4)).append(ffByteArr.at(i*4 + 1)).append(ffByteArr.at(i*4 + 2)).append(ffByteArr.at(i*4 + 3)); + ffArr[i] = ByteUtil::binToFloat(tempBa); + } + + float sim = casic::face::CasicFaceInterface::getInstance().faceSimCalculate(CasicFaceRecState::getInstance().faceInfo->feature, ffArr); + + LOG(DEBUG) << QString("[compareFaceInCollection] 特征值比对结果[%1]").arg(sim).toStdString(); + if (sim > 0.62) + { + CasicFaceRecState::getInstance().faceInfo->sim = sim; + CasicFaceRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + CasicFaceRecState::getInstance().recogTimeLast = (float) ((CasicFaceRecState::getInstance().timeStampSucc - CasicFaceRecState::getInstance().timeStamp) / 1000.0); + CasicFaceRecState::getInstance().state = CasicFaceRecState::FaceRecStateName::REC_SEARCH_SUCC; + + // 找到匹配的人脸图像 + emit successCaptureFace(ffMap.value("person_id").toString()); + return ; + } + } + + // 没有找到匹配的人脸 + emit successCaptureFace(""); + } +} + diff --git a/device/face/FaceDetectRegistProcess.h b/device/face/FaceDetectRegistProcess.h index 5d42bbe..4df7708 100644 --- a/device/face/FaceDetectRegistProcess.h +++ b/device/face/FaceDetectRegistProcess.h @@ -2,26 +2,48 @@ #define FACEDETECTREGISTPROCESS_H #include +#include #include "opencv2/opencv.hpp" #include "utils/easyloggingpp/easylogging++.h" +#include "utils/ImageUtil.h" +#include "utils/SettingConfig.h" +#include "utils/ByteUtil.h" #include "CasicFaceRecState.h" +#include "casic/ProMemory.h" #include "casic/face/CasicFaceInterface.h" static bool FACE_DETECT_FLAG = false; -class FaceDetectRegistProcess : public QObject +class FaceDetectRegistProcess : public QThread { Q_OBJECT public: explicit FaceDetectRegistProcess(QObject *parent = nullptr); + ~FaceDetectRegistProcess(); -public slots: - void faceDetect(cv::Mat faceMat); + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoFaceCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + void compareFaceInCollection(); + + volatile bool working; + volatile bool exit; signals: - void extractFeatureSuccess(CasicFaceRecState& faceRecState); + void extractFeatureSuccess(); + void failedCaptureFace(); + void successCaptureFace(QString personId); + void findSimFace(QString personId); }; diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp index 7604572..3a4c678 100644 --- a/utils/ImageUtil.cpp +++ b/utils/ImageUtil.cpp @@ -79,7 +79,7 @@ QString base64String = ba.toBase64(); return base64String; } - +*/ cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) { cv::Mat matClone = src.clone(); @@ -94,4 +94,4 @@ return roi; } -*/ + diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h index dbfdd48..5ef3c13 100644 --- a/utils/ImageUtil.h +++ b/utils/ImageUtil.h @@ -16,7 +16,7 @@ // static cv::Mat QImageToMat(QImage image); // static QString QImageToBase64(QImage image); -// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); + static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); }; #endif // IMAGEUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..2cb9b20 --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,18 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} diff --git a/utils/SpeakerUtil.h b/utils/SpeakerUtil.h new file mode 100644 index 0000000..d4c1074 --- /dev/null +++ b/utils/SpeakerUtil.h @@ -0,0 +1,31 @@ +#ifndef SPEAKERUTIL_H +#define SPEAKERUTIL_H + +#include +#include + +class SpeakerUtil : public QObject +{ + Q_OBJECT +public: + ~SpeakerUtil() {}; + SpeakerUtil(const SpeakerUtil&)=delete; + SpeakerUtil& operator=(const SpeakerUtil&)=delete; + + static SpeakerUtil& getInstance() { + static SpeakerUtil instance; + return instance; + } + + void speak(QString content); + +private: + explicit SpeakerUtil(QObject *parent = nullptr); + + QTextToSpeech * tts; + +signals: + +}; + +#endif // SPEAKERUTIL_H diff --git a/utils/utils.pri b/utils/utils.pri index 384cda1..d4f4e4a 100644 --- a/utils/utils.pri +++ b/utils/utils.pri @@ -19,14 +19,19 @@ HEADERS += $$PWD/ImageUtil.h SOURCES += $$PWD/ImageUtil.cpp -#HEADERS += $$PWD/QByteUtil.h +HEADERS += $$PWD/SpeakerUtil.h +SOURCES += $$PWD/SpeakerUtil.cpp + +HEADERS += $$PWD/ByteUtil.h +SOURCES += $$PWD/ByteUtil.cpp + #HEADERS += $$PWD/QSocketClientUtil.h #HEADERS += $$PWD/TimerCounter.h -#HEADERS += $$PWD/SpeakerUtil.h -#SOURCES += $$PWD/QByteUtil.cpp + + #SOURCES += $$PWD/QSocketClientUtil.cpp #SOURCES += $$PWD/TimerCounter.cpp -#SOURCES += $$PWD/SpeakerUtil.cpp +