diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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/FaceRegistProcess.h b/device/face/FaceRegistProcess.h new file mode 100644 index 0000000..aa45763 --- /dev/null +++ b/device/face/FaceRegistProcess.h @@ -0,0 +1,42 @@ +#ifndef FACEREGISTPROCESS_H +#define FACEREGISTPROCESS_H + +#include +#include +#include "opencv2/opencv.hpp" + +#include "CasicFaceRecState.h" +#include "casic/ProMemory.h" +#include "casic/face/CasicFaceInterface.h" +#include "utils/UtilInclude.h" + +class FaceRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit FaceRegistProcess(QObject *parent = nullptr); + ~FaceRegistProcess(); + + 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 failedCaptureFace(); + void successCaptureFace(QString personIdByFace); + +}; + +#endif // FACEREGISTPROCESS_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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/FaceRegistProcess.h b/device/face/FaceRegistProcess.h new file mode 100644 index 0000000..aa45763 --- /dev/null +++ b/device/face/FaceRegistProcess.h @@ -0,0 +1,42 @@ +#ifndef FACEREGISTPROCESS_H +#define FACEREGISTPROCESS_H + +#include +#include +#include "opencv2/opencv.hpp" + +#include "CasicFaceRecState.h" +#include "casic/ProMemory.h" +#include "casic/face/CasicFaceInterface.h" +#include "utils/UtilInclude.h" + +class FaceRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit FaceRegistProcess(QObject *parent = nullptr); + ~FaceRegistProcess(); + + 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 failedCaptureFace(); + void successCaptureFace(QString personIdByFace); + +}; + +#endif // FACEREGISTPROCESS_H diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..28625a1 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,61 @@ +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noEyeCount = 0; + + LOG(DEBUG) << QString("[CasicIrisRecState][initRecognize] 虹膜识别状态初始化").toStdString(); +} + +std::string CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)).toStdString(); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromStdString(recoginzeId)); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject eyeObj; + eyeObj.insert("hasEye", irisInfo->hasEye); + eyeObj.insert("leftOrRight", irisInfo->leftOrRight); + QJsonArray rectArray; + rectArray.append(irisInfo->eyeRect.x); + rectArray.append(irisInfo->eyeRect.y); + rectArray.append(irisInfo->eyeRect.width); + rectArray.append(irisInfo->eyeRect.height); + eyeObj.insert("faceRect", rectArray); + + obj.insert("eye", eyeObj); + + return obj; +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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/FaceRegistProcess.h b/device/face/FaceRegistProcess.h new file mode 100644 index 0000000..aa45763 --- /dev/null +++ b/device/face/FaceRegistProcess.h @@ -0,0 +1,42 @@ +#ifndef FACEREGISTPROCESS_H +#define FACEREGISTPROCESS_H + +#include +#include +#include "opencv2/opencv.hpp" + +#include "CasicFaceRecState.h" +#include "casic/ProMemory.h" +#include "casic/face/CasicFaceInterface.h" +#include "utils/UtilInclude.h" + +class FaceRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit FaceRegistProcess(QObject *parent = nullptr); + ~FaceRegistProcess(); + + 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 failedCaptureFace(); + void successCaptureFace(QString personIdByFace); + +}; + +#endif // FACEREGISTPROCESS_H diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..28625a1 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,61 @@ +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noEyeCount = 0; + + LOG(DEBUG) << QString("[CasicIrisRecState][initRecognize] 虹膜识别状态初始化").toStdString(); +} + +std::string CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)).toStdString(); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromStdString(recoginzeId)); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject eyeObj; + eyeObj.insert("hasEye", irisInfo->hasEye); + eyeObj.insert("leftOrRight", irisInfo->leftOrRight); + QJsonArray rectArray; + rectArray.append(irisInfo->eyeRect.x); + rectArray.append(irisInfo->eyeRect.y); + rectArray.append(irisInfo->eyeRect.width); + rectArray.append(irisInfo->eyeRect.height); + eyeObj.insert("faceRect", rectArray); + + obj.insert("eye", eyeObj); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..43bf234 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,67 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + std::string toString(); + QJsonObject toJSON(); + + std::string recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + + CasicIrisInfo * irisInfo; + + qint8 tryCount = 0; // 识别尝试次数 + qint8 noEyeCount = 0; // 连续没有找到眼睛次数 + float recogTimeLast = 0.0; // 识别成功耗时 + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_LEFT_FIND_EYE = 1, // 检测左眼, 没找到右眼 + REC_RIGHT_FIND_EYE = 2, // 检测右眼, 没找到左眼 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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/FaceRegistProcess.h b/device/face/FaceRegistProcess.h new file mode 100644 index 0000000..aa45763 --- /dev/null +++ b/device/face/FaceRegistProcess.h @@ -0,0 +1,42 @@ +#ifndef FACEREGISTPROCESS_H +#define FACEREGISTPROCESS_H + +#include +#include +#include "opencv2/opencv.hpp" + +#include "CasicFaceRecState.h" +#include "casic/ProMemory.h" +#include "casic/face/CasicFaceInterface.h" +#include "utils/UtilInclude.h" + +class FaceRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit FaceRegistProcess(QObject *parent = nullptr); + ~FaceRegistProcess(); + + 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 failedCaptureFace(); + void successCaptureFace(QString personIdByFace); + +}; + +#endif // FACEREGISTPROCESS_H diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..28625a1 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,61 @@ +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noEyeCount = 0; + + LOG(DEBUG) << QString("[CasicIrisRecState][initRecognize] 虹膜识别状态初始化").toStdString(); +} + +std::string CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)).toStdString(); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromStdString(recoginzeId)); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject eyeObj; + eyeObj.insert("hasEye", irisInfo->hasEye); + eyeObj.insert("leftOrRight", irisInfo->leftOrRight); + QJsonArray rectArray; + rectArray.append(irisInfo->eyeRect.x); + rectArray.append(irisInfo->eyeRect.y); + rectArray.append(irisInfo->eyeRect.width); + rectArray.append(irisInfo->eyeRect.height); + eyeObj.insert("faceRect", rectArray); + + obj.insert("eye", eyeObj); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..43bf234 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,67 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + std::string toString(); + QJsonObject toJSON(); + + std::string recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + + CasicIrisInfo * irisInfo; + + qint8 tryCount = 0; // 识别尝试次数 + qint8 noEyeCount = 0; // 连续没有找到眼睛次数 + float recogTimeLast = 0.0; // 识别成功耗时 + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_LEFT_FIND_EYE = 1, // 检测左眼, 没找到右眼 + REC_RIGHT_FIND_EYE = 2, // 检测右眼, 没找到左眼 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRegistProcess.cpp b/device/iris/IrisRegistProcess.cpp new file mode 100644 index 0000000..7696cf3 --- /dev/null +++ b/device/iris/IrisRegistProcess.cpp @@ -0,0 +1,180 @@ +#include "IrisRegistProcess.h" + +IrisRegistProcess::IrisRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect("127.0.0.1", 2015); + + // 调用socket发送消息 + connect(this, &IrisRegistProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRegistProcess::~IrisRegistProcess() +{ + this->setWorking(false); +} + +void IrisRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void IrisRegistProcess::exitThread() +{ + this->exit = true; +} + +void IrisRegistProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; + LOG(DEBUG) << QString("[CasicIrisRegistProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); + + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + emit captureIrisFailure(); + } else { + ProMemory::getInstance().irisRegistPro->setWorking(true); // 继续工作 + } +} +void IrisRegistProcess::addOneNoEyeCount() +{ + CasicIrisRecState::getInstance().noEyeCount++; + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + emit captureIrisFailure(); + } +} + +void IrisRegistProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(200); // 200ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + LOG(DEBUG) << QString("[CasicIrisRegistThread]虹膜图像栈空 暂停200ms").toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出一幅图像 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + LOG(DEBUG) << QString("[CasicIrisRegistThread]取出一幅虹膜图").toStdString(); + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE && + irisInfo.leftOrRight == "left") + { + // 左眼已经编码成功则不再判断左眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]左眼已经编码成功则不再判断左眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } else if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE && + irisInfo.leftOrRight == "right"){ + // 右眼已经编码成功则不再判断右眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]右眼已经编码成功则不再判断右眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 调用找眼分类器算法 + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + + // 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicIrisRegistThread]分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount).toStdString(); + addOneNoEyeCount(); + + // 双眼都要注册, 没找到眼不重置状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到眼睛则暂停线程工作 + ProMemory::getInstance().irisRegistPro->setWorking(false); + LOG(DEBUG) << QString("找到眼睛暂停线程工作").toStdString(); + + // 找到眼睛则开始质量评估和编码 + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; + CasicIrisRecState::getInstance().irisInfo->leftOrRight = irisInfo.leftOrRight; + CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; + CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; + CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + + QElapsedTimer timer; + timer.start(); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); + data.prepend("[-r]"); // 注册编码 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + QByteArray encodeResponse = clientUtil->getResponse(); + + // 清除队列缓存 + ProMemory::getInstance().clearIrisQueue(); + + // 编码成功 + if (encodeResponse.size() == 1024) + { + LOG(DEBUG) << QString("虹膜特征值提取[耗时%1ms][%2]").arg(timer.elapsed()).arg(irisInfo.leftOrRight).toStdString(); + + // 保存图像文件 + cv::imwrite(QString("D:\\irisLogs\\%1-%2.bmp").arg(QDateTime::currentDateTime().toString("HHmmsszzz")).arg(irisInfo.leftOrRight).toStdString(), irisInfo.matData); + + CasicIrisRecState::getInstance().irisInfo->irisCode = encodeResponse; + + // 编码成功 到业务中判断 + emit findNoSimIris(CasicIrisRecState::getInstance().irisInfo); + } else if (encodeResponse.contains("-100") == true) + { + // 编码失败 + addOneTryCount(); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + } else if (encodeResponse.contains("-101") == true || + encodeResponse.contains("-102") == true) + { + // 找到匹配的人 + QString message = encodeResponse.mid(5, encodeResponse.size() - 6); + QStringList messList = message.split(";"); + QString personId = messList.at(1); + + // 到业务中进行判断 + emit findMatchedIris(personId); + } else + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + // 超时返回则尝试次数+1 + addOneTryCount(); + } + } +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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/FaceRegistProcess.h b/device/face/FaceRegistProcess.h new file mode 100644 index 0000000..aa45763 --- /dev/null +++ b/device/face/FaceRegistProcess.h @@ -0,0 +1,42 @@ +#ifndef FACEREGISTPROCESS_H +#define FACEREGISTPROCESS_H + +#include +#include +#include "opencv2/opencv.hpp" + +#include "CasicFaceRecState.h" +#include "casic/ProMemory.h" +#include "casic/face/CasicFaceInterface.h" +#include "utils/UtilInclude.h" + +class FaceRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit FaceRegistProcess(QObject *parent = nullptr); + ~FaceRegistProcess(); + + 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 failedCaptureFace(); + void successCaptureFace(QString personIdByFace); + +}; + +#endif // FACEREGISTPROCESS_H diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..28625a1 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,61 @@ +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noEyeCount = 0; + + LOG(DEBUG) << QString("[CasicIrisRecState][initRecognize] 虹膜识别状态初始化").toStdString(); +} + +std::string CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)).toStdString(); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromStdString(recoginzeId)); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject eyeObj; + eyeObj.insert("hasEye", irisInfo->hasEye); + eyeObj.insert("leftOrRight", irisInfo->leftOrRight); + QJsonArray rectArray; + rectArray.append(irisInfo->eyeRect.x); + rectArray.append(irisInfo->eyeRect.y); + rectArray.append(irisInfo->eyeRect.width); + rectArray.append(irisInfo->eyeRect.height); + eyeObj.insert("faceRect", rectArray); + + obj.insert("eye", eyeObj); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..43bf234 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,67 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + std::string toString(); + QJsonObject toJSON(); + + std::string recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + + CasicIrisInfo * irisInfo; + + qint8 tryCount = 0; // 识别尝试次数 + qint8 noEyeCount = 0; // 连续没有找到眼睛次数 + float recogTimeLast = 0.0; // 识别成功耗时 + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_LEFT_FIND_EYE = 1, // 检测左眼, 没找到右眼 + REC_RIGHT_FIND_EYE = 2, // 检测右眼, 没找到左眼 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRegistProcess.cpp b/device/iris/IrisRegistProcess.cpp new file mode 100644 index 0000000..7696cf3 --- /dev/null +++ b/device/iris/IrisRegistProcess.cpp @@ -0,0 +1,180 @@ +#include "IrisRegistProcess.h" + +IrisRegistProcess::IrisRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect("127.0.0.1", 2015); + + // 调用socket发送消息 + connect(this, &IrisRegistProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRegistProcess::~IrisRegistProcess() +{ + this->setWorking(false); +} + +void IrisRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void IrisRegistProcess::exitThread() +{ + this->exit = true; +} + +void IrisRegistProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; + LOG(DEBUG) << QString("[CasicIrisRegistProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); + + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + emit captureIrisFailure(); + } else { + ProMemory::getInstance().irisRegistPro->setWorking(true); // 继续工作 + } +} +void IrisRegistProcess::addOneNoEyeCount() +{ + CasicIrisRecState::getInstance().noEyeCount++; + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + emit captureIrisFailure(); + } +} + +void IrisRegistProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(200); // 200ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + LOG(DEBUG) << QString("[CasicIrisRegistThread]虹膜图像栈空 暂停200ms").toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出一幅图像 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + LOG(DEBUG) << QString("[CasicIrisRegistThread]取出一幅虹膜图").toStdString(); + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE && + irisInfo.leftOrRight == "left") + { + // 左眼已经编码成功则不再判断左眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]左眼已经编码成功则不再判断左眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } else if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE && + irisInfo.leftOrRight == "right"){ + // 右眼已经编码成功则不再判断右眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]右眼已经编码成功则不再判断右眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 调用找眼分类器算法 + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + + // 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicIrisRegistThread]分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount).toStdString(); + addOneNoEyeCount(); + + // 双眼都要注册, 没找到眼不重置状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到眼睛则暂停线程工作 + ProMemory::getInstance().irisRegistPro->setWorking(false); + LOG(DEBUG) << QString("找到眼睛暂停线程工作").toStdString(); + + // 找到眼睛则开始质量评估和编码 + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; + CasicIrisRecState::getInstance().irisInfo->leftOrRight = irisInfo.leftOrRight; + CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; + CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; + CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + + QElapsedTimer timer; + timer.start(); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); + data.prepend("[-r]"); // 注册编码 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + QByteArray encodeResponse = clientUtil->getResponse(); + + // 清除队列缓存 + ProMemory::getInstance().clearIrisQueue(); + + // 编码成功 + if (encodeResponse.size() == 1024) + { + LOG(DEBUG) << QString("虹膜特征值提取[耗时%1ms][%2]").arg(timer.elapsed()).arg(irisInfo.leftOrRight).toStdString(); + + // 保存图像文件 + cv::imwrite(QString("D:\\irisLogs\\%1-%2.bmp").arg(QDateTime::currentDateTime().toString("HHmmsszzz")).arg(irisInfo.leftOrRight).toStdString(), irisInfo.matData); + + CasicIrisRecState::getInstance().irisInfo->irisCode = encodeResponse; + + // 编码成功 到业务中判断 + emit findNoSimIris(CasicIrisRecState::getInstance().irisInfo); + } else if (encodeResponse.contains("-100") == true) + { + // 编码失败 + addOneTryCount(); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + } else if (encodeResponse.contains("-101") == true || + encodeResponse.contains("-102") == true) + { + // 找到匹配的人 + QString message = encodeResponse.mid(5, encodeResponse.size() - 6); + QStringList messList = message.split(";"); + QString personId = messList.at(1); + + // 到业务中进行判断 + emit findMatchedIris(personId); + } else + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + // 超时返回则尝试次数+1 + addOneTryCount(); + } + } +} diff --git a/device/iris/IrisRegistProcess.h b/device/iris/IrisRegistProcess.h new file mode 100644 index 0000000..5533387 --- /dev/null +++ b/device/iris/IrisRegistProcess.h @@ -0,0 +1,45 @@ +#ifndef IRISREGISTPROCESS_H +#define IRISREGISTPROCESS_H + +#include +#include +#include +#include + +#include "casic/ProMemory.h" +#include "device/iris/CasicIrisRecState.h" + +class IrisRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRegistProcess(QObject *parent = nullptr); + ~IrisRegistProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + +signals: + void sendDataToExract(QByteArray data); + + void findNoSimIris(CasicIrisInfo * irisInfo); + void findMatchedIris(QString personId); + void captureIrisFailure(); +}; + +#endif // IRISREGISTPROCESS_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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/FaceRegistProcess.h b/device/face/FaceRegistProcess.h new file mode 100644 index 0000000..aa45763 --- /dev/null +++ b/device/face/FaceRegistProcess.h @@ -0,0 +1,42 @@ +#ifndef FACEREGISTPROCESS_H +#define FACEREGISTPROCESS_H + +#include +#include +#include "opencv2/opencv.hpp" + +#include "CasicFaceRecState.h" +#include "casic/ProMemory.h" +#include "casic/face/CasicFaceInterface.h" +#include "utils/UtilInclude.h" + +class FaceRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit FaceRegistProcess(QObject *parent = nullptr); + ~FaceRegistProcess(); + + 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 failedCaptureFace(); + void successCaptureFace(QString personIdByFace); + +}; + +#endif // FACEREGISTPROCESS_H diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..28625a1 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,61 @@ +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noEyeCount = 0; + + LOG(DEBUG) << QString("[CasicIrisRecState][initRecognize] 虹膜识别状态初始化").toStdString(); +} + +std::string CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)).toStdString(); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromStdString(recoginzeId)); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject eyeObj; + eyeObj.insert("hasEye", irisInfo->hasEye); + eyeObj.insert("leftOrRight", irisInfo->leftOrRight); + QJsonArray rectArray; + rectArray.append(irisInfo->eyeRect.x); + rectArray.append(irisInfo->eyeRect.y); + rectArray.append(irisInfo->eyeRect.width); + rectArray.append(irisInfo->eyeRect.height); + eyeObj.insert("faceRect", rectArray); + + obj.insert("eye", eyeObj); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..43bf234 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,67 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + std::string toString(); + QJsonObject toJSON(); + + std::string recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + + CasicIrisInfo * irisInfo; + + qint8 tryCount = 0; // 识别尝试次数 + qint8 noEyeCount = 0; // 连续没有找到眼睛次数 + float recogTimeLast = 0.0; // 识别成功耗时 + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_LEFT_FIND_EYE = 1, // 检测左眼, 没找到右眼 + REC_RIGHT_FIND_EYE = 2, // 检测右眼, 没找到左眼 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRegistProcess.cpp b/device/iris/IrisRegistProcess.cpp new file mode 100644 index 0000000..7696cf3 --- /dev/null +++ b/device/iris/IrisRegistProcess.cpp @@ -0,0 +1,180 @@ +#include "IrisRegistProcess.h" + +IrisRegistProcess::IrisRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect("127.0.0.1", 2015); + + // 调用socket发送消息 + connect(this, &IrisRegistProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRegistProcess::~IrisRegistProcess() +{ + this->setWorking(false); +} + +void IrisRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void IrisRegistProcess::exitThread() +{ + this->exit = true; +} + +void IrisRegistProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; + LOG(DEBUG) << QString("[CasicIrisRegistProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); + + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + emit captureIrisFailure(); + } else { + ProMemory::getInstance().irisRegistPro->setWorking(true); // 继续工作 + } +} +void IrisRegistProcess::addOneNoEyeCount() +{ + CasicIrisRecState::getInstance().noEyeCount++; + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + emit captureIrisFailure(); + } +} + +void IrisRegistProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(200); // 200ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + LOG(DEBUG) << QString("[CasicIrisRegistThread]虹膜图像栈空 暂停200ms").toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出一幅图像 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + LOG(DEBUG) << QString("[CasicIrisRegistThread]取出一幅虹膜图").toStdString(); + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE && + irisInfo.leftOrRight == "left") + { + // 左眼已经编码成功则不再判断左眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]左眼已经编码成功则不再判断左眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } else if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE && + irisInfo.leftOrRight == "right"){ + // 右眼已经编码成功则不再判断右眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]右眼已经编码成功则不再判断右眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 调用找眼分类器算法 + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + + // 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicIrisRegistThread]分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount).toStdString(); + addOneNoEyeCount(); + + // 双眼都要注册, 没找到眼不重置状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到眼睛则暂停线程工作 + ProMemory::getInstance().irisRegistPro->setWorking(false); + LOG(DEBUG) << QString("找到眼睛暂停线程工作").toStdString(); + + // 找到眼睛则开始质量评估和编码 + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; + CasicIrisRecState::getInstance().irisInfo->leftOrRight = irisInfo.leftOrRight; + CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; + CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; + CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + + QElapsedTimer timer; + timer.start(); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); + data.prepend("[-r]"); // 注册编码 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + QByteArray encodeResponse = clientUtil->getResponse(); + + // 清除队列缓存 + ProMemory::getInstance().clearIrisQueue(); + + // 编码成功 + if (encodeResponse.size() == 1024) + { + LOG(DEBUG) << QString("虹膜特征值提取[耗时%1ms][%2]").arg(timer.elapsed()).arg(irisInfo.leftOrRight).toStdString(); + + // 保存图像文件 + cv::imwrite(QString("D:\\irisLogs\\%1-%2.bmp").arg(QDateTime::currentDateTime().toString("HHmmsszzz")).arg(irisInfo.leftOrRight).toStdString(), irisInfo.matData); + + CasicIrisRecState::getInstance().irisInfo->irisCode = encodeResponse; + + // 编码成功 到业务中判断 + emit findNoSimIris(CasicIrisRecState::getInstance().irisInfo); + } else if (encodeResponse.contains("-100") == true) + { + // 编码失败 + addOneTryCount(); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + } else if (encodeResponse.contains("-101") == true || + encodeResponse.contains("-102") == true) + { + // 找到匹配的人 + QString message = encodeResponse.mid(5, encodeResponse.size() - 6); + QStringList messList = message.split(";"); + QString personId = messList.at(1); + + // 到业务中进行判断 + emit findMatchedIris(personId); + } else + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + // 超时返回则尝试次数+1 + addOneTryCount(); + } + } +} diff --git a/device/iris/IrisRegistProcess.h b/device/iris/IrisRegistProcess.h new file mode 100644 index 0000000..5533387 --- /dev/null +++ b/device/iris/IrisRegistProcess.h @@ -0,0 +1,45 @@ +#ifndef IRISREGISTPROCESS_H +#define IRISREGISTPROCESS_H + +#include +#include +#include +#include + +#include "casic/ProMemory.h" +#include "device/iris/CasicIrisRecState.h" + +class IrisRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRegistProcess(QObject *parent = nullptr); + ~IrisRegistProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + +signals: + void sendDataToExract(QByteArray data); + + void findNoSimIris(CasicIrisInfo * irisInfo); + void findMatchedIris(QString personId); + void captureIrisFailure(); +}; + +#endif // IRISREGISTPROCESS_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp index 3a4c678..3d565fa 100644 --- a/utils/ImageUtil.cpp +++ b/utils/ImageUtil.cpp @@ -43,7 +43,7 @@ //在不改变实际图像数据的条件下, 交换红蓝通道 return qImage.rgbSwapped(); } - +*/ cv::Mat ImageUtil::QImageToMat(QImage image) { cv::Mat mat; @@ -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(); diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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/FaceRegistProcess.h b/device/face/FaceRegistProcess.h new file mode 100644 index 0000000..aa45763 --- /dev/null +++ b/device/face/FaceRegistProcess.h @@ -0,0 +1,42 @@ +#ifndef FACEREGISTPROCESS_H +#define FACEREGISTPROCESS_H + +#include +#include +#include "opencv2/opencv.hpp" + +#include "CasicFaceRecState.h" +#include "casic/ProMemory.h" +#include "casic/face/CasicFaceInterface.h" +#include "utils/UtilInclude.h" + +class FaceRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit FaceRegistProcess(QObject *parent = nullptr); + ~FaceRegistProcess(); + + 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 failedCaptureFace(); + void successCaptureFace(QString personIdByFace); + +}; + +#endif // FACEREGISTPROCESS_H diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..28625a1 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,61 @@ +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noEyeCount = 0; + + LOG(DEBUG) << QString("[CasicIrisRecState][initRecognize] 虹膜识别状态初始化").toStdString(); +} + +std::string CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)).toStdString(); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromStdString(recoginzeId)); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject eyeObj; + eyeObj.insert("hasEye", irisInfo->hasEye); + eyeObj.insert("leftOrRight", irisInfo->leftOrRight); + QJsonArray rectArray; + rectArray.append(irisInfo->eyeRect.x); + rectArray.append(irisInfo->eyeRect.y); + rectArray.append(irisInfo->eyeRect.width); + rectArray.append(irisInfo->eyeRect.height); + eyeObj.insert("faceRect", rectArray); + + obj.insert("eye", eyeObj); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..43bf234 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,67 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + std::string toString(); + QJsonObject toJSON(); + + std::string recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + + CasicIrisInfo * irisInfo; + + qint8 tryCount = 0; // 识别尝试次数 + qint8 noEyeCount = 0; // 连续没有找到眼睛次数 + float recogTimeLast = 0.0; // 识别成功耗时 + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_LEFT_FIND_EYE = 1, // 检测左眼, 没找到右眼 + REC_RIGHT_FIND_EYE = 2, // 检测右眼, 没找到左眼 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRegistProcess.cpp b/device/iris/IrisRegistProcess.cpp new file mode 100644 index 0000000..7696cf3 --- /dev/null +++ b/device/iris/IrisRegistProcess.cpp @@ -0,0 +1,180 @@ +#include "IrisRegistProcess.h" + +IrisRegistProcess::IrisRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect("127.0.0.1", 2015); + + // 调用socket发送消息 + connect(this, &IrisRegistProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRegistProcess::~IrisRegistProcess() +{ + this->setWorking(false); +} + +void IrisRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void IrisRegistProcess::exitThread() +{ + this->exit = true; +} + +void IrisRegistProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; + LOG(DEBUG) << QString("[CasicIrisRegistProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); + + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + emit captureIrisFailure(); + } else { + ProMemory::getInstance().irisRegistPro->setWorking(true); // 继续工作 + } +} +void IrisRegistProcess::addOneNoEyeCount() +{ + CasicIrisRecState::getInstance().noEyeCount++; + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + emit captureIrisFailure(); + } +} + +void IrisRegistProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(200); // 200ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + LOG(DEBUG) << QString("[CasicIrisRegistThread]虹膜图像栈空 暂停200ms").toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出一幅图像 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + LOG(DEBUG) << QString("[CasicIrisRegistThread]取出一幅虹膜图").toStdString(); + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE && + irisInfo.leftOrRight == "left") + { + // 左眼已经编码成功则不再判断左眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]左眼已经编码成功则不再判断左眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } else if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE && + irisInfo.leftOrRight == "right"){ + // 右眼已经编码成功则不再判断右眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]右眼已经编码成功则不再判断右眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 调用找眼分类器算法 + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + + // 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicIrisRegistThread]分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount).toStdString(); + addOneNoEyeCount(); + + // 双眼都要注册, 没找到眼不重置状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到眼睛则暂停线程工作 + ProMemory::getInstance().irisRegistPro->setWorking(false); + LOG(DEBUG) << QString("找到眼睛暂停线程工作").toStdString(); + + // 找到眼睛则开始质量评估和编码 + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; + CasicIrisRecState::getInstance().irisInfo->leftOrRight = irisInfo.leftOrRight; + CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; + CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; + CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + + QElapsedTimer timer; + timer.start(); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); + data.prepend("[-r]"); // 注册编码 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + QByteArray encodeResponse = clientUtil->getResponse(); + + // 清除队列缓存 + ProMemory::getInstance().clearIrisQueue(); + + // 编码成功 + if (encodeResponse.size() == 1024) + { + LOG(DEBUG) << QString("虹膜特征值提取[耗时%1ms][%2]").arg(timer.elapsed()).arg(irisInfo.leftOrRight).toStdString(); + + // 保存图像文件 + cv::imwrite(QString("D:\\irisLogs\\%1-%2.bmp").arg(QDateTime::currentDateTime().toString("HHmmsszzz")).arg(irisInfo.leftOrRight).toStdString(), irisInfo.matData); + + CasicIrisRecState::getInstance().irisInfo->irisCode = encodeResponse; + + // 编码成功 到业务中判断 + emit findNoSimIris(CasicIrisRecState::getInstance().irisInfo); + } else if (encodeResponse.contains("-100") == true) + { + // 编码失败 + addOneTryCount(); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + } else if (encodeResponse.contains("-101") == true || + encodeResponse.contains("-102") == true) + { + // 找到匹配的人 + QString message = encodeResponse.mid(5, encodeResponse.size() - 6); + QStringList messList = message.split(";"); + QString personId = messList.at(1); + + // 到业务中进行判断 + emit findMatchedIris(personId); + } else + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + // 超时返回则尝试次数+1 + addOneTryCount(); + } + } +} diff --git a/device/iris/IrisRegistProcess.h b/device/iris/IrisRegistProcess.h new file mode 100644 index 0000000..5533387 --- /dev/null +++ b/device/iris/IrisRegistProcess.h @@ -0,0 +1,45 @@ +#ifndef IRISREGISTPROCESS_H +#define IRISREGISTPROCESS_H + +#include +#include +#include +#include + +#include "casic/ProMemory.h" +#include "device/iris/CasicIrisRecState.h" + +class IrisRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRegistProcess(QObject *parent = nullptr); + ~IrisRegistProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + +signals: + void sendDataToExract(QByteArray data); + + void findNoSimIris(CasicIrisInfo * irisInfo); + void findMatchedIris(QString personId); + void captureIrisFailure(); +}; + +#endif // IRISREGISTPROCESS_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp index 3a4c678..3d565fa 100644 --- a/utils/ImageUtil.cpp +++ b/utils/ImageUtil.cpp @@ -43,7 +43,7 @@ //在不改变实际图像数据的条件下, 交换红蓝通道 return qImage.rgbSwapped(); } - +*/ cv::Mat ImageUtil::QImageToMat(QImage image) { cv::Mat mat; @@ -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(); diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h index 5ef3c13..731fc48 100644 --- a/utils/ImageUtil.h +++ b/utils/ImageUtil.h @@ -13,8 +13,8 @@ static QImage MatImageToQImage(const cv::Mat &src); // static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); -// static cv::Mat QImageToMat(QImage image); -// static QString QImageToBase64(QImage image); + static cv::Mat QImageToMat(QImage image); + static QString QImageToBase64(QImage image); static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); }; diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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/FaceRegistProcess.h b/device/face/FaceRegistProcess.h new file mode 100644 index 0000000..aa45763 --- /dev/null +++ b/device/face/FaceRegistProcess.h @@ -0,0 +1,42 @@ +#ifndef FACEREGISTPROCESS_H +#define FACEREGISTPROCESS_H + +#include +#include +#include "opencv2/opencv.hpp" + +#include "CasicFaceRecState.h" +#include "casic/ProMemory.h" +#include "casic/face/CasicFaceInterface.h" +#include "utils/UtilInclude.h" + +class FaceRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit FaceRegistProcess(QObject *parent = nullptr); + ~FaceRegistProcess(); + + 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 failedCaptureFace(); + void successCaptureFace(QString personIdByFace); + +}; + +#endif // FACEREGISTPROCESS_H diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..28625a1 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,61 @@ +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noEyeCount = 0; + + LOG(DEBUG) << QString("[CasicIrisRecState][initRecognize] 虹膜识别状态初始化").toStdString(); +} + +std::string CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)).toStdString(); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromStdString(recoginzeId)); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject eyeObj; + eyeObj.insert("hasEye", irisInfo->hasEye); + eyeObj.insert("leftOrRight", irisInfo->leftOrRight); + QJsonArray rectArray; + rectArray.append(irisInfo->eyeRect.x); + rectArray.append(irisInfo->eyeRect.y); + rectArray.append(irisInfo->eyeRect.width); + rectArray.append(irisInfo->eyeRect.height); + eyeObj.insert("faceRect", rectArray); + + obj.insert("eye", eyeObj); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..43bf234 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,67 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + std::string toString(); + QJsonObject toJSON(); + + std::string recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + + CasicIrisInfo * irisInfo; + + qint8 tryCount = 0; // 识别尝试次数 + qint8 noEyeCount = 0; // 连续没有找到眼睛次数 + float recogTimeLast = 0.0; // 识别成功耗时 + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_LEFT_FIND_EYE = 1, // 检测左眼, 没找到右眼 + REC_RIGHT_FIND_EYE = 2, // 检测右眼, 没找到左眼 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRegistProcess.cpp b/device/iris/IrisRegistProcess.cpp new file mode 100644 index 0000000..7696cf3 --- /dev/null +++ b/device/iris/IrisRegistProcess.cpp @@ -0,0 +1,180 @@ +#include "IrisRegistProcess.h" + +IrisRegistProcess::IrisRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect("127.0.0.1", 2015); + + // 调用socket发送消息 + connect(this, &IrisRegistProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRegistProcess::~IrisRegistProcess() +{ + this->setWorking(false); +} + +void IrisRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void IrisRegistProcess::exitThread() +{ + this->exit = true; +} + +void IrisRegistProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; + LOG(DEBUG) << QString("[CasicIrisRegistProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); + + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + emit captureIrisFailure(); + } else { + ProMemory::getInstance().irisRegistPro->setWorking(true); // 继续工作 + } +} +void IrisRegistProcess::addOneNoEyeCount() +{ + CasicIrisRecState::getInstance().noEyeCount++; + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + emit captureIrisFailure(); + } +} + +void IrisRegistProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(200); // 200ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + LOG(DEBUG) << QString("[CasicIrisRegistThread]虹膜图像栈空 暂停200ms").toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出一幅图像 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + LOG(DEBUG) << QString("[CasicIrisRegistThread]取出一幅虹膜图").toStdString(); + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE && + irisInfo.leftOrRight == "left") + { + // 左眼已经编码成功则不再判断左眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]左眼已经编码成功则不再判断左眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } else if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE && + irisInfo.leftOrRight == "right"){ + // 右眼已经编码成功则不再判断右眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]右眼已经编码成功则不再判断右眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 调用找眼分类器算法 + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + + // 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicIrisRegistThread]分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount).toStdString(); + addOneNoEyeCount(); + + // 双眼都要注册, 没找到眼不重置状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到眼睛则暂停线程工作 + ProMemory::getInstance().irisRegistPro->setWorking(false); + LOG(DEBUG) << QString("找到眼睛暂停线程工作").toStdString(); + + // 找到眼睛则开始质量评估和编码 + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; + CasicIrisRecState::getInstance().irisInfo->leftOrRight = irisInfo.leftOrRight; + CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; + CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; + CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + + QElapsedTimer timer; + timer.start(); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); + data.prepend("[-r]"); // 注册编码 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + QByteArray encodeResponse = clientUtil->getResponse(); + + // 清除队列缓存 + ProMemory::getInstance().clearIrisQueue(); + + // 编码成功 + if (encodeResponse.size() == 1024) + { + LOG(DEBUG) << QString("虹膜特征值提取[耗时%1ms][%2]").arg(timer.elapsed()).arg(irisInfo.leftOrRight).toStdString(); + + // 保存图像文件 + cv::imwrite(QString("D:\\irisLogs\\%1-%2.bmp").arg(QDateTime::currentDateTime().toString("HHmmsszzz")).arg(irisInfo.leftOrRight).toStdString(), irisInfo.matData); + + CasicIrisRecState::getInstance().irisInfo->irisCode = encodeResponse; + + // 编码成功 到业务中判断 + emit findNoSimIris(CasicIrisRecState::getInstance().irisInfo); + } else if (encodeResponse.contains("-100") == true) + { + // 编码失败 + addOneTryCount(); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + } else if (encodeResponse.contains("-101") == true || + encodeResponse.contains("-102") == true) + { + // 找到匹配的人 + QString message = encodeResponse.mid(5, encodeResponse.size() - 6); + QStringList messList = message.split(";"); + QString personId = messList.at(1); + + // 到业务中进行判断 + emit findMatchedIris(personId); + } else + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + // 超时返回则尝试次数+1 + addOneTryCount(); + } + } +} diff --git a/device/iris/IrisRegistProcess.h b/device/iris/IrisRegistProcess.h new file mode 100644 index 0000000..5533387 --- /dev/null +++ b/device/iris/IrisRegistProcess.h @@ -0,0 +1,45 @@ +#ifndef IRISREGISTPROCESS_H +#define IRISREGISTPROCESS_H + +#include +#include +#include +#include + +#include "casic/ProMemory.h" +#include "device/iris/CasicIrisRecState.h" + +class IrisRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRegistProcess(QObject *parent = nullptr); + ~IrisRegistProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + +signals: + void sendDataToExract(QByteArray data); + + void findNoSimIris(CasicIrisInfo * irisInfo); + void findMatchedIris(QString personId); + void captureIrisFailure(); +}; + +#endif // IRISREGISTPROCESS_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp index 3a4c678..3d565fa 100644 --- a/utils/ImageUtil.cpp +++ b/utils/ImageUtil.cpp @@ -43,7 +43,7 @@ //在不改变实际图像数据的条件下, 交换红蓝通道 return qImage.rgbSwapped(); } - +*/ cv::Mat ImageUtil::QImageToMat(QImage image) { cv::Mat mat; @@ -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(); diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h index 5ef3c13..731fc48 100644 --- a/utils/ImageUtil.h +++ b/utils/ImageUtil.h @@ -13,8 +13,8 @@ static QImage MatImageToQImage(const cv::Mat &src); // static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); -// static cv::Mat QImageToMat(QImage image); -// static QString QImageToBase64(QImage image); + static cv::Mat QImageToMat(QImage image); + static QString QImageToBase64(QImage image); static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); }; diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..d1ef79a --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,34 @@ +#include "SocketClientUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, + this, &SocketClientUtil::readData); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::sendData(QByteArray data) +{ + data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + + this->response = buffer; + emit this->responseReaded(); +} diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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/FaceRegistProcess.h b/device/face/FaceRegistProcess.h new file mode 100644 index 0000000..aa45763 --- /dev/null +++ b/device/face/FaceRegistProcess.h @@ -0,0 +1,42 @@ +#ifndef FACEREGISTPROCESS_H +#define FACEREGISTPROCESS_H + +#include +#include +#include "opencv2/opencv.hpp" + +#include "CasicFaceRecState.h" +#include "casic/ProMemory.h" +#include "casic/face/CasicFaceInterface.h" +#include "utils/UtilInclude.h" + +class FaceRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit FaceRegistProcess(QObject *parent = nullptr); + ~FaceRegistProcess(); + + 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 failedCaptureFace(); + void successCaptureFace(QString personIdByFace); + +}; + +#endif // FACEREGISTPROCESS_H diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..28625a1 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,61 @@ +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noEyeCount = 0; + + LOG(DEBUG) << QString("[CasicIrisRecState][initRecognize] 虹膜识别状态初始化").toStdString(); +} + +std::string CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)).toStdString(); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromStdString(recoginzeId)); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject eyeObj; + eyeObj.insert("hasEye", irisInfo->hasEye); + eyeObj.insert("leftOrRight", irisInfo->leftOrRight); + QJsonArray rectArray; + rectArray.append(irisInfo->eyeRect.x); + rectArray.append(irisInfo->eyeRect.y); + rectArray.append(irisInfo->eyeRect.width); + rectArray.append(irisInfo->eyeRect.height); + eyeObj.insert("faceRect", rectArray); + + obj.insert("eye", eyeObj); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..43bf234 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,67 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + std::string toString(); + QJsonObject toJSON(); + + std::string recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + + CasicIrisInfo * irisInfo; + + qint8 tryCount = 0; // 识别尝试次数 + qint8 noEyeCount = 0; // 连续没有找到眼睛次数 + float recogTimeLast = 0.0; // 识别成功耗时 + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_LEFT_FIND_EYE = 1, // 检测左眼, 没找到右眼 + REC_RIGHT_FIND_EYE = 2, // 检测右眼, 没找到左眼 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRegistProcess.cpp b/device/iris/IrisRegistProcess.cpp new file mode 100644 index 0000000..7696cf3 --- /dev/null +++ b/device/iris/IrisRegistProcess.cpp @@ -0,0 +1,180 @@ +#include "IrisRegistProcess.h" + +IrisRegistProcess::IrisRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect("127.0.0.1", 2015); + + // 调用socket发送消息 + connect(this, &IrisRegistProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRegistProcess::~IrisRegistProcess() +{ + this->setWorking(false); +} + +void IrisRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void IrisRegistProcess::exitThread() +{ + this->exit = true; +} + +void IrisRegistProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; + LOG(DEBUG) << QString("[CasicIrisRegistProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); + + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + emit captureIrisFailure(); + } else { + ProMemory::getInstance().irisRegistPro->setWorking(true); // 继续工作 + } +} +void IrisRegistProcess::addOneNoEyeCount() +{ + CasicIrisRecState::getInstance().noEyeCount++; + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + emit captureIrisFailure(); + } +} + +void IrisRegistProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(200); // 200ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + LOG(DEBUG) << QString("[CasicIrisRegistThread]虹膜图像栈空 暂停200ms").toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出一幅图像 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + LOG(DEBUG) << QString("[CasicIrisRegistThread]取出一幅虹膜图").toStdString(); + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE && + irisInfo.leftOrRight == "left") + { + // 左眼已经编码成功则不再判断左眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]左眼已经编码成功则不再判断左眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } else if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE && + irisInfo.leftOrRight == "right"){ + // 右眼已经编码成功则不再判断右眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]右眼已经编码成功则不再判断右眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 调用找眼分类器算法 + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + + // 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicIrisRegistThread]分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount).toStdString(); + addOneNoEyeCount(); + + // 双眼都要注册, 没找到眼不重置状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到眼睛则暂停线程工作 + ProMemory::getInstance().irisRegistPro->setWorking(false); + LOG(DEBUG) << QString("找到眼睛暂停线程工作").toStdString(); + + // 找到眼睛则开始质量评估和编码 + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; + CasicIrisRecState::getInstance().irisInfo->leftOrRight = irisInfo.leftOrRight; + CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; + CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; + CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + + QElapsedTimer timer; + timer.start(); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); + data.prepend("[-r]"); // 注册编码 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + QByteArray encodeResponse = clientUtil->getResponse(); + + // 清除队列缓存 + ProMemory::getInstance().clearIrisQueue(); + + // 编码成功 + if (encodeResponse.size() == 1024) + { + LOG(DEBUG) << QString("虹膜特征值提取[耗时%1ms][%2]").arg(timer.elapsed()).arg(irisInfo.leftOrRight).toStdString(); + + // 保存图像文件 + cv::imwrite(QString("D:\\irisLogs\\%1-%2.bmp").arg(QDateTime::currentDateTime().toString("HHmmsszzz")).arg(irisInfo.leftOrRight).toStdString(), irisInfo.matData); + + CasicIrisRecState::getInstance().irisInfo->irisCode = encodeResponse; + + // 编码成功 到业务中判断 + emit findNoSimIris(CasicIrisRecState::getInstance().irisInfo); + } else if (encodeResponse.contains("-100") == true) + { + // 编码失败 + addOneTryCount(); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + } else if (encodeResponse.contains("-101") == true || + encodeResponse.contains("-102") == true) + { + // 找到匹配的人 + QString message = encodeResponse.mid(5, encodeResponse.size() - 6); + QStringList messList = message.split(";"); + QString personId = messList.at(1); + + // 到业务中进行判断 + emit findMatchedIris(personId); + } else + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + // 超时返回则尝试次数+1 + addOneTryCount(); + } + } +} diff --git a/device/iris/IrisRegistProcess.h b/device/iris/IrisRegistProcess.h new file mode 100644 index 0000000..5533387 --- /dev/null +++ b/device/iris/IrisRegistProcess.h @@ -0,0 +1,45 @@ +#ifndef IRISREGISTPROCESS_H +#define IRISREGISTPROCESS_H + +#include +#include +#include +#include + +#include "casic/ProMemory.h" +#include "device/iris/CasicIrisRecState.h" + +class IrisRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRegistProcess(QObject *parent = nullptr); + ~IrisRegistProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + +signals: + void sendDataToExract(QByteArray data); + + void findNoSimIris(CasicIrisInfo * irisInfo); + void findMatchedIris(QString personId); + void captureIrisFailure(); +}; + +#endif // IRISREGISTPROCESS_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp index 3a4c678..3d565fa 100644 --- a/utils/ImageUtil.cpp +++ b/utils/ImageUtil.cpp @@ -43,7 +43,7 @@ //在不改变实际图像数据的条件下, 交换红蓝通道 return qImage.rgbSwapped(); } - +*/ cv::Mat ImageUtil::QImageToMat(QImage image) { cv::Mat mat; @@ -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(); diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h index 5ef3c13..731fc48 100644 --- a/utils/ImageUtil.h +++ b/utils/ImageUtil.h @@ -13,8 +13,8 @@ static QImage MatImageToQImage(const cv::Mat &src); // static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); -// static cv::Mat QImageToMat(QImage image); -// static QString QImageToBase64(QImage image); + static cv::Mat QImageToMat(QImage image); + static QString QImageToBase64(QImage image); static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); }; diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..d1ef79a --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,34 @@ +#include "SocketClientUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, + this, &SocketClientUtil::readData); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::sendData(QByteArray data) +{ + data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + + this->response = buffer; + emit this->responseReaded(); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..c3cf6e4 --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,33 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + QByteArray getResponse(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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/FaceRegistProcess.h b/device/face/FaceRegistProcess.h new file mode 100644 index 0000000..aa45763 --- /dev/null +++ b/device/face/FaceRegistProcess.h @@ -0,0 +1,42 @@ +#ifndef FACEREGISTPROCESS_H +#define FACEREGISTPROCESS_H + +#include +#include +#include "opencv2/opencv.hpp" + +#include "CasicFaceRecState.h" +#include "casic/ProMemory.h" +#include "casic/face/CasicFaceInterface.h" +#include "utils/UtilInclude.h" + +class FaceRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit FaceRegistProcess(QObject *parent = nullptr); + ~FaceRegistProcess(); + + 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 failedCaptureFace(); + void successCaptureFace(QString personIdByFace); + +}; + +#endif // FACEREGISTPROCESS_H diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..28625a1 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,61 @@ +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noEyeCount = 0; + + LOG(DEBUG) << QString("[CasicIrisRecState][initRecognize] 虹膜识别状态初始化").toStdString(); +} + +std::string CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)).toStdString(); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromStdString(recoginzeId)); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject eyeObj; + eyeObj.insert("hasEye", irisInfo->hasEye); + eyeObj.insert("leftOrRight", irisInfo->leftOrRight); + QJsonArray rectArray; + rectArray.append(irisInfo->eyeRect.x); + rectArray.append(irisInfo->eyeRect.y); + rectArray.append(irisInfo->eyeRect.width); + rectArray.append(irisInfo->eyeRect.height); + eyeObj.insert("faceRect", rectArray); + + obj.insert("eye", eyeObj); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..43bf234 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,67 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + std::string toString(); + QJsonObject toJSON(); + + std::string recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + + CasicIrisInfo * irisInfo; + + qint8 tryCount = 0; // 识别尝试次数 + qint8 noEyeCount = 0; // 连续没有找到眼睛次数 + float recogTimeLast = 0.0; // 识别成功耗时 + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_LEFT_FIND_EYE = 1, // 检测左眼, 没找到右眼 + REC_RIGHT_FIND_EYE = 2, // 检测右眼, 没找到左眼 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRegistProcess.cpp b/device/iris/IrisRegistProcess.cpp new file mode 100644 index 0000000..7696cf3 --- /dev/null +++ b/device/iris/IrisRegistProcess.cpp @@ -0,0 +1,180 @@ +#include "IrisRegistProcess.h" + +IrisRegistProcess::IrisRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect("127.0.0.1", 2015); + + // 调用socket发送消息 + connect(this, &IrisRegistProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRegistProcess::~IrisRegistProcess() +{ + this->setWorking(false); +} + +void IrisRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void IrisRegistProcess::exitThread() +{ + this->exit = true; +} + +void IrisRegistProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; + LOG(DEBUG) << QString("[CasicIrisRegistProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); + + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + emit captureIrisFailure(); + } else { + ProMemory::getInstance().irisRegistPro->setWorking(true); // 继续工作 + } +} +void IrisRegistProcess::addOneNoEyeCount() +{ + CasicIrisRecState::getInstance().noEyeCount++; + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + emit captureIrisFailure(); + } +} + +void IrisRegistProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(200); // 200ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + LOG(DEBUG) << QString("[CasicIrisRegistThread]虹膜图像栈空 暂停200ms").toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出一幅图像 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + LOG(DEBUG) << QString("[CasicIrisRegistThread]取出一幅虹膜图").toStdString(); + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE && + irisInfo.leftOrRight == "left") + { + // 左眼已经编码成功则不再判断左眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]左眼已经编码成功则不再判断左眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } else if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE && + irisInfo.leftOrRight == "right"){ + // 右眼已经编码成功则不再判断右眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]右眼已经编码成功则不再判断右眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 调用找眼分类器算法 + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + + // 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicIrisRegistThread]分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount).toStdString(); + addOneNoEyeCount(); + + // 双眼都要注册, 没找到眼不重置状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到眼睛则暂停线程工作 + ProMemory::getInstance().irisRegistPro->setWorking(false); + LOG(DEBUG) << QString("找到眼睛暂停线程工作").toStdString(); + + // 找到眼睛则开始质量评估和编码 + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; + CasicIrisRecState::getInstance().irisInfo->leftOrRight = irisInfo.leftOrRight; + CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; + CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; + CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + + QElapsedTimer timer; + timer.start(); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); + data.prepend("[-r]"); // 注册编码 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + QByteArray encodeResponse = clientUtil->getResponse(); + + // 清除队列缓存 + ProMemory::getInstance().clearIrisQueue(); + + // 编码成功 + if (encodeResponse.size() == 1024) + { + LOG(DEBUG) << QString("虹膜特征值提取[耗时%1ms][%2]").arg(timer.elapsed()).arg(irisInfo.leftOrRight).toStdString(); + + // 保存图像文件 + cv::imwrite(QString("D:\\irisLogs\\%1-%2.bmp").arg(QDateTime::currentDateTime().toString("HHmmsszzz")).arg(irisInfo.leftOrRight).toStdString(), irisInfo.matData); + + CasicIrisRecState::getInstance().irisInfo->irisCode = encodeResponse; + + // 编码成功 到业务中判断 + emit findNoSimIris(CasicIrisRecState::getInstance().irisInfo); + } else if (encodeResponse.contains("-100") == true) + { + // 编码失败 + addOneTryCount(); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + } else if (encodeResponse.contains("-101") == true || + encodeResponse.contains("-102") == true) + { + // 找到匹配的人 + QString message = encodeResponse.mid(5, encodeResponse.size() - 6); + QStringList messList = message.split(";"); + QString personId = messList.at(1); + + // 到业务中进行判断 + emit findMatchedIris(personId); + } else + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + // 超时返回则尝试次数+1 + addOneTryCount(); + } + } +} diff --git a/device/iris/IrisRegistProcess.h b/device/iris/IrisRegistProcess.h new file mode 100644 index 0000000..5533387 --- /dev/null +++ b/device/iris/IrisRegistProcess.h @@ -0,0 +1,45 @@ +#ifndef IRISREGISTPROCESS_H +#define IRISREGISTPROCESS_H + +#include +#include +#include +#include + +#include "casic/ProMemory.h" +#include "device/iris/CasicIrisRecState.h" + +class IrisRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRegistProcess(QObject *parent = nullptr); + ~IrisRegistProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + +signals: + void sendDataToExract(QByteArray data); + + void findNoSimIris(CasicIrisInfo * irisInfo); + void findMatchedIris(QString personId); + void captureIrisFailure(); +}; + +#endif // IRISREGISTPROCESS_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp index 3a4c678..3d565fa 100644 --- a/utils/ImageUtil.cpp +++ b/utils/ImageUtil.cpp @@ -43,7 +43,7 @@ //在不改变实际图像数据的条件下, 交换红蓝通道 return qImage.rgbSwapped(); } - +*/ cv::Mat ImageUtil::QImageToMat(QImage image) { cv::Mat mat; @@ -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(); diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h index 5ef3c13..731fc48 100644 --- a/utils/ImageUtil.h +++ b/utils/ImageUtil.h @@ -13,8 +13,8 @@ static QImage MatImageToQImage(const cv::Mat &src); // static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); -// static cv::Mat QImageToMat(QImage image); -// static QString QImageToBase64(QImage image); + static cv::Mat QImageToMat(QImage image); + static QString QImageToBase64(QImage image); static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); }; diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..d1ef79a --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,34 @@ +#include "SocketClientUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, + this, &SocketClientUtil::readData); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::sendData(QByteArray data) +{ + data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + + this->response = buffer; + emit this->responseReaded(); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..c3cf6e4 --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,33 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + QByteArray getResponse(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/UtilInclude.h b/utils/UtilInclude.h index f3ded58..6019de0 100644 --- a/utils/UtilInclude.h +++ b/utils/UtilInclude.h @@ -8,5 +8,6 @@ #include "SettingConfig.h" #include "SpeakerUtil.h" #include "TimeCounterUtil.h" +#include "SocketClientUtil.h" #endif // UTILINCLUDE_H diff --git a/AddPersonForm.cpp b/AddPersonForm.cpp index 64c9b5c..d14fd54 100644 --- a/AddPersonForm.cpp +++ b/AddPersonForm.cpp @@ -1,6 +1,7 @@ #include "AddPersonForm.h" #include "ui_AddPersonForm.h" + AddPersonForm::AddPersonForm(QWidget *parent) : QWidget(parent), ui(new Ui::AddPersonForm) @@ -21,10 +22,15 @@ faceLabel = new QLabel(this); faceLabel->hide(); + leftIrisLabel = new QLabel(this); + leftIrisLabel->hide(); + rightIrisLabel = new QLabel(this); + rightIrisLabel->hide(); + // 绑定图像双击槽函数 connect(ui->labPhotoFace, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoFaceDoubleClicked); connect(ui->labPhotoEyeLeft, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); - connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::on_btnHome_clicked); + connect(ui->labPhotoEyeRight, &QDblClickLabel::doubleClicked, this, &AddPersonForm::onPhotoIrisDoubleClicked); } AddPersonForm::~AddPersonForm() @@ -160,6 +166,19 @@ faceLabel->setPixmap(QPixmap::fromImage(imageDisp)); } } +void AddPersonForm::drawIrisImageOnFrame(QImage image, int leftOrRight) +{ + if (this->leftIrisLabel->isVisible() == true) + { + if (leftOrRight == 0) + { + leftIrisLabel->setPixmap(QPixmap::fromImage(image)); + } else if (leftOrRight == 1) + { + rightIrisLabel->setPixmap(QPixmap::fromImage(image)); + } + } +} void AddPersonForm::onFailedCaptureFace() { @@ -246,6 +265,85 @@ } } +void AddPersonForm::onFailedCaptureIris() +{ + // 隐藏虹膜画面显示界面 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜采集失败,请重试")); +} + +void AddPersonForm::onMatchedIris(QString personIdByIris) +{ + // 找到重复的人 提示需要重新采集 + leftIrisLabel->hide(); + rightIrisLabel->hide(); + + // 停止拍图 + ProMemory::getInstance().irisCam->stopCapture(); + + ProMemory::getInstance().irisRegistPro->setWorking(false); + + // 语音提示 + SpeakerUtil::getInstance().speak(QString("虹膜重复注册,请重试")); + + if (this->personId.isEmpty() == true) + { + // 情况1 新注册 重复注册 + loadPersonInfo(personIdByIris); + LOG(DEBUG) << "虹膜已经注册" << ui->inputName->text().toStdString(); + } else { + LOG(DEBUG) << "虹膜已经注册" << personIdByIris.toStdString(); + } +} + +void AddPersonForm::onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo) +{ + LOG(INFO) << QString("[AddPersonForm]虹膜编码成功[%1][%2]").arg(irisInfo->leftOrRight).arg(CasicIrisRecState::getInstance().state).toStdString(); + updateIrisImage(irisInfo); + + if (irisInfo->leftOrRight == "left") + { + // 已经找到右眼, 又找到左眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("左眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 左眼采集成功 重新开始工作 + } + } else + { + // 已经找到左眼, 又找到右眼则编码成功 + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE) + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + + SpeakerUtil::getInstance().speak("虹膜采集成功"); + ProMemory::getInstance().irisCam->stopCapture(); + this->irisReCaptured = true; + } else + { + SpeakerUtil::getInstance().speak("右眼虹膜编码成功"); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 右眼采集成功 重新开始工作 + } + } +} + bool AddPersonForm::validateForm() { // 验证姓名字段 @@ -315,7 +413,7 @@ } QString faceCodeId = faceDataDao.save(faceVar); - if (faceImgId.toULongLong() < 0 && faceCodeId.toULongLong() < 0) + if (faceImgId.toULongLong() < 0 || faceCodeId.toULongLong() < 0) { OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(false); @@ -334,6 +432,41 @@ } // 注册虹膜信息 + if (leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + IrisDataDao irisDao; + IrisDataImgDao irisImgDao; + + QVariantMap irisVar; + irisVar.insert("person_id", perIdReg); + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + QString irisImgId = irisImgDao.save(irisVar); + if (irisImgId.toULongLong() > 0) + { + irisVar.insert("image_id", irisImgId); + } + + QString irisCodeId = irisDao.save(irisVar); + if (irisImgId.toULongLong() < 0 || irisCodeId.toULongLong() < 0) + { + OperationTipsDialog tipsDlg; + tipsDlg.setTipsDialogType(false); + tipsDlg.setTipsText(QString("%1 虹膜保存失败").arg(ui->inputName->text())); + tipsDlg.exec(); + return; + } else { + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, perIdReg); + } + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } // 弹出提示框 bool succ = perIdReg == "-1" ? false : true; @@ -423,6 +556,59 @@ ProMemory::getInstance().initFaceFeatures(); } + // 需要更新虹膜数据 + if (irisReCaptured == true && + leftIrisCode.isEmpty() == false && rightIrisCode.isEmpty() == false) + { + QVariantMap irisVar; + irisVar.insert("left_image1", leftIris); + irisVar.insert("right_image1", rightIris); + irisVar.insert("left_iris_code1", leftIrisCode); + irisVar.insert("right_iris_code1", rightIrisCode); + + QString irisImgId; + QString irisCodeId; + + // 更新虹膜图像数据 + IrisDataImgDao irisImgDao; + QVariantMap irisImageRec = irisImgDao.findRecordByPersonId(personId); // 查找人脸照片数据库记录 + if (irisImageRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸图像 + irisVar.insert("person_id", personId); + irisImgId = irisImgDao.save(irisVar); + } else { + // 找到一条记录 + irisImgId = irisImageRec.value("id").toString(); + irisImgDao.edit(irisVar, irisImgId); + } + + // 更新虹膜特征值数据 + IrisDataDao irisDao; + QVariantMap irisCodeRec = irisDao.findRecordByPersonId(personId); // 查找人脸特征值数据库记录 + if (irisCodeRec.isEmpty() == true) + { + // 没有找到记录 需要新增一条人脸特征值 + irisVar.insert("person_id", personId); + irisVar.insert("image_id", irisImgId); + irisCodeId = irisDao.save(irisVar); + } else { + // 找到一条记录 + irisCodeId = irisCodeRec.value("id").toString(); + irisDao.edit(irisVar, irisCodeId); + } + + this->irisReCaptured = false; + + // 虹膜保存成功 更新SYS_PERSON表的虹膜字段 + QVariantMap irisValid; + irisValid.insert("iris_valid", "1"); + personDao.edit(irisValid, personId); + + // 重新加载虹膜库 + ProMemory::getInstance().initIrisFeatures(); + } + // 弹出提示框 OperationTipsDialog tipsDlg; tipsDlg.setTipsDialogType(succ); @@ -432,6 +618,33 @@ LOG(INFO) << QString("%1 人员信息编辑%2").arg(ui->inputName->text()).arg(succ == true ? "成功" : "失败").toStdString(); } +void AddPersonForm::updateIrisImage(CasicIrisInfo * irisInfo) +{ + if (irisInfo->leftOrRight == "left") + { + // 显示左眼的虹膜图像 + QPixmap pmLeft = QPixmap::fromImage(irisInfo->data); + pmLeft = pmLeft.scaled(192, 144); + ui->labPhotoEyeLeft->setPixmap(pmLeft); + + leftIris = ImageUtil::QImageToBase64(irisInfo->data); + + // 虹膜特征值字节数组 + leftIrisCode = irisInfo->irisCode; + leftIrisLabel->hide(); + } else if (irisInfo->leftOrRight == "right") + { + // 设置显示左右虹膜照片 + QPixmap pmRight = QPixmap::fromImage(irisInfo->data); + pmRight = pmRight.scaled(192, 144); + ui->labPhotoEyeRight->setPixmap(pmRight); + + rightIris = ImageUtil::QImageToBase64(irisInfo->data); + rightIrisCode = irisInfo->irisCode; + rightIrisLabel->hide(); + } +} + void AddPersonForm::on_btnBack_clicked() { emit switchToUserListForm(); @@ -477,5 +690,28 @@ void AddPersonForm::onPhotoIrisDoubleClicked() { - LOG(DEBUG) << "DOUBLE CLICKED IRIS"; + LOG(TRACE) << "DOUBLE CLICKED IRIS " << personId.toStdString(); + + // 编辑时先发指令重新加载虹膜库 去掉自己 + ProMemory::getInstance().initIrisFeatures(personId); + + // 0. 语音提示开始采集 + SpeakerUtil::getInstance().speak(QString("采集虹膜")); + + // 开始拍图 + ProMemory::getInstance().irisCam->startCapture(); + + // 1虹膜图像显示的容器 + leftIrisLabel->resize(640, 480); + leftIrisLabel->move(0, 80); + leftIrisLabel->raise(); + leftIrisLabel->show(); + + rightIrisLabel->resize(640, 480); + rightIrisLabel->move(640, 80); + rightIrisLabel->raise(); + rightIrisLabel->show(); + + ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::ADD_PERSON_CAPTURE_IRIS; + ProMemory::getInstance().irisRegistPro->setWorking(true); // 开始工作 } diff --git a/AddPersonForm.h b/AddPersonForm.h index e552bdf..8b5cd9b 100644 --- a/AddPersonForm.h +++ b/AddPersonForm.h @@ -31,8 +31,14 @@ public slots: void drawImageOnForm(QImage imageDisp); - void onFailedCaptureFace(); - void onSuccessCaptureFace(QString personIdByFace); + void drawIrisImageOnFrame(QImage image, int leftOrRight); + + void onFailedCaptureFace(); // 采集人脸失败 + void onSuccessCaptureFace(QString personIdByFace); // 采集人脸成功 没有匹配/有匹配到 + + void onFailedCaptureIris(); // 采集虹膜失败 + void onMatchedIris(QString personIdByIris); // 采集虹膜成功 有匹配 + void onSuccessCaptureIrisWithoutMatch(CasicIrisInfo * irisInfo); // 采集虹膜成功 没有匹配到 private slots: void on_btnBack_clicked(); @@ -53,11 +59,20 @@ QByteArray faceCode; // 人脸特征编码数据 bool faceReCaptured; // 编辑时是否需要更新人脸数据 + QString leftIris; // 左眼虹膜图像数据 + QString rightIris; // 右眼虹膜图像数据 + QByteArray leftIrisCode; // 左眼虹膜特征值编码数据 + QByteArray rightIrisCode; // 右眼虹膜特征值编码数据 + bool irisReCaptured; // 编辑时是否需要更新虹膜数据 + QLabel * faceLabel; // 采集人脸时显示的画面 + QLabel * leftIrisLabel; // 采集时左眼显示的画面 + QLabel * rightIrisLabel; // 采集时右眼显示的画面 bool validateForm(); void registPerson(); void editPersonInfo(); + void updateIrisImage(CasicIrisInfo * irisInfo); signals: void switchToUserListForm(); diff --git a/CasicBioRecNew.pro b/CasicBioRecNew.pro index 65f1946..6633b0d 100644 --- a/CasicBioRecNew.pro +++ b/CasicBioRecNew.pro @@ -72,3 +72,8 @@ INCLUDEPATH += $$PWD/../../opencv420/build/include DEPENDPATH += $$PWD/../../opencv420/build/include + +win32: LIBS += -L$$PWD/daheng/lib/ -lGxIAPICPPEx + +INCLUDEPATH += $$PWD/daheng/include +DEPENDPATH += $$PWD/daheng/include diff --git a/CasicBioRecWin.cpp b/CasicBioRecWin.cpp index efc73fd..45fe761 100644 --- a/CasicBioRecWin.cpp +++ b/CasicBioRecWin.cpp @@ -5,6 +5,10 @@ : QMainWindow(parent) , ui(new Ui::CasicBioRecWin) { + // 打开外部可执行程序 + outExe = new QProcess(this); +// outExe->start(SettingConfig::getInstance().OUT_EXE_FILE); + ui->setupUi(this); // 程序启动时初始化缓存的数据 @@ -25,6 +29,7 @@ // 初始化识别和注册的各个线程 initFaceRegistThread(); // 人脸注册线程 initFaceRecogThread(); // 人脸识别线程 + initIrisRegistThread(); // 虹膜注册线程 // 初始化人脸相机控制 ProMemory::getInstance().faceCam = new FaceCameraController(this); @@ -34,6 +39,15 @@ connect(ProMemory::getInstance().faceCam, &FaceCameraController::sendImageToDrawForRecognize, recogResultForm, &RecognizeResultForm::drawImageOnHomeForm); + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCam = new IrisCameraController(this); + ProMemory::getInstance().irisCam->initIrisCamera(); + ProMemory::getInstance().irisCam->openIrisCamera(); + connect(ProMemory::getInstance().irisCam->leftHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + connect(ProMemory::getInstance().irisCam->rightHandler, &IrisCameraCapEventHandler::sendIrisFrameToDraw, + addPersonForm, &AddPersonForm::drawIrisImageOnFrame); + // 打印日志 LOG(INFO) << QString("应用程序启动成功[Application Startup Success]").toStdString(); ProMemory::getInstance().widgeFrame = CasicBioRecConst::WidgeFrameName::MAIN_PAGE; @@ -49,9 +63,18 @@ ProMemory::getInstance().faceRecogPro->deleteLater(); ProMemory::getInstance().faceRecogPro->wait(); + ProMemory::getInstance().irisRegistPro->exitThread(); + ProMemory::getInstance().irisRegistPro->deleteLater(); + ProMemory::getInstance().irisRegistPro->wait(); + + // 关闭自动启动的外部程序 +// outExe->close(); +// delete outExe; + delete ui; delete ProMemory::getInstance().faceRegistPro; delete ProMemory::getInstance().faceRecogPro; + delete ProMemory::getInstance().irisRegistPro; } void CasicBioRecWin::keyPressEvent(QKeyEvent *event) @@ -154,15 +177,15 @@ void CasicBioRecWin::initFaceRegistThread() { // 人脸注册处理过程 - ProMemory::getInstance().faceRegistPro = new FaceDetectRegistProcess(this); + ProMemory::getInstance().faceRegistPro = new FaceRegistProcess(this); // 绑定信号与槽函数 // 采集人脸图像失败 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::failedCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::failedCaptureFace, addPersonForm, &AddPersonForm::onFailedCaptureFace); // 采集人脸图像成功 - connect(ProMemory::getInstance().faceRegistPro, &FaceDetectRegistProcess::successCaptureFace, + connect(ProMemory::getInstance().faceRegistPro, &FaceRegistProcess::successCaptureFace, addPersonForm, &AddPersonForm::onSuccessCaptureFace); ProMemory::getInstance().faceRegistPro->start(); @@ -184,3 +207,24 @@ ProMemory::getInstance().faceRecogPro->start(); } + +void CasicBioRecWin::initIrisRegistThread() +{ + // 虹膜注册处理过程 + ProMemory::getInstance().irisRegistPro = new IrisRegistProcess(this); + + // 绑定信号与槽函数 + // 没有找到匹配的虹膜 采集成功 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findNoSimIris, + addPersonForm, &AddPersonForm::onSuccessCaptureIrisWithoutMatch); + + // 采集虹膜失败 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::captureIrisFailure, + addPersonForm, &AddPersonForm::onFailedCaptureIris); + + // 采集虹膜图像成功 有匹配到红魔库 + connect(ProMemory::getInstance().irisRegistPro, &IrisRegistProcess::findMatchedIris, + addPersonForm, &AddPersonForm::onMatchedIris); + + ProMemory::getInstance().irisRegistPro->start(); +} diff --git a/CasicBioRecWin.h b/CasicBioRecWin.h index f0b47bf..ff2ed35 100644 --- a/CasicBioRecWin.h +++ b/CasicBioRecWin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "casic/ProMemory.h" #include "dao/util/CacheManager.h" @@ -15,7 +16,6 @@ #include "AddPersonForm.h" #include "RecognizeResultForm.h" -#include "device/FaceCameraController.h" QT_BEGIN_NAMESPACE namespace Ui { class CasicBioRecWin; } @@ -39,6 +39,8 @@ private: Ui::CasicBioRecWin *ui; + QProcess * outExe; + StartupForm * startForm; PersonListForm * personListForm; SettingForm * settingForm; @@ -50,6 +52,7 @@ void initCacheData(); void initFaceRegistThread(); void initFaceRecogThread(); + void initIrisRegistThread(); }; #endif // CASICBIORECWIN_H diff --git a/QDblClickLabel.cpp b/QDblClickLabel.cpp index d68899c..f21d32b 100644 --- a/QDblClickLabel.cpp +++ b/QDblClickLabel.cpp @@ -14,3 +14,14 @@ { emit doubleClicked(); } + +void QDblClickLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (event != nullptr) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(); + } + } +} diff --git a/QDblClickLabel.h b/QDblClickLabel.h index bd7c532..c2a6f45 100644 --- a/QDblClickLabel.h +++ b/QDblClickLabel.h @@ -3,6 +3,7 @@ #include #include +#include class QDblClickLabel : public QLabel { @@ -11,9 +12,11 @@ QDblClickLabel(QWidget * parent = 0); ~QDblClickLabel(); void mouseDoubleClickEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); signals: void doubleClicked(); + void clicked(); }; #endif // QDBLCLICKLABEL_H diff --git a/StartupForm.cpp b/StartupForm.cpp index 272e8f1..3d3dfad 100644 --- a/StartupForm.cpp +++ b/StartupForm.cpp @@ -29,8 +29,8 @@ // 每秒执行一次 connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &StartupForm::updateDateAndTime); - connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, - this, &StartupForm::takeOneFrameForCheckStatus); +// connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, +// this, &StartupForm::takeOneFrameForCheckStatus); TimeCounterUtil::getInstance().clockCounter->start(1000); } diff --git a/casic/ProMemory.cpp b/casic/ProMemory.cpp index 789bf94..f72b9ff 100644 --- a/casic/ProMemory.cpp +++ b/casic/ProMemory.cpp @@ -1,4 +1,4 @@ -#include "ProMemory.h" +#include "ProMemory.h" ProMemory::ProMemory() { @@ -85,34 +85,34 @@ } -//void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) -//{ -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 30) -// { -// QStack empty; -// swap(empty, irisQueue); -// } +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 30) + { + QStack empty; + std::swap(empty, irisQueue); + } -// irisQueue.push(irisInfo); -// mutex.unlock(); -//} -//CasicIrisInfo ProMemory::popCasicIris() -//{ -// CasicIrisInfo result; + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; -// QMutex mutex; -// mutex.lock(); -// if (this->irisQueue.size() > 0) -// { -// result = irisQueue.pop(); -// } + QMutex mutex; + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } -// mutex.unlock(); + mutex.unlock(); -// return result; -//} + return result; +} int ProMemory::getIrisQueueSize() { int size = 0; @@ -120,7 +120,7 @@ QMutex mutex; mutex.lock(); -// size = this->irisQueue.size(); + size = this->irisQueue.size(); mutex.unlock(); @@ -133,7 +133,7 @@ QMutex mutex; mutex.lock(); -// empty = this->irisQueue.isEmpty(); + empty = this->irisQueue.isEmpty(); mutex.unlock(); @@ -144,8 +144,8 @@ QMutex mutex; mutex.lock(); -// QStack empty; -// swap(empty, irisQueue); + QStack empty; + std::swap(empty, irisQueue); mutex.unlock(); } @@ -156,7 +156,7 @@ this->faceFeatures = faceDataDao.findAllRecord(); } -void ProMemory::initIrisFeatures() +void ProMemory::initIrisFeatures(QString personId) { - + irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } diff --git a/casic/ProMemory.h b/casic/ProMemory.h index 6454343..1bc7d3a 100644 --- a/casic/ProMemory.h +++ b/casic/ProMemory.h @@ -1,4 +1,4 @@ -#ifndef PROMEMORY_H +#ifndef PROMEMORY_H #define PROMEMORY_H #include @@ -6,17 +6,21 @@ #include "CasicBioRecConst.h" #include "casic/face/CasicFaceInfo.h" -//#include "casic/iris/CasicIrisInfo.h" +#include "casic/iris/CasicIrisInfo.h" #include "dao/FaceDataDao.h" #include "dao/IrisDataDao.h" #include "device/FaceCameraController.h" -#include "device/face/FaceDetectRegistProcess.h" +#include "device/IrisCameraController.h" +#include "device/face/FaceRegistProcess.h" #include "device/face/FaceDetectRecogProcess.h" +#include "device/iris/IrisRegistProcess.h" class FaceCameraController; -class FaceDetectRegistProcess; +class IrisCameraController; +class FaceRegistProcess; class FaceDetectRecogProcess; +class IrisRegistProcess; class ProMemory { @@ -37,8 +41,8 @@ bool isFaceQueueEmpty(); void clearFaceQueue(); -// void pushCasicIris(CasicIrisInfo irisInfo); -// CasicIrisInfo popCasicIris(); + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); int getIrisQueueSize(); bool isIrisQueueEmpty(); void clearIrisQueue(); @@ -48,19 +52,22 @@ QString capFaceOrIris; // 注册虹膜或者人脸 void initFaceFeatures(); // 初始化人脸特征值集合 - void initIrisFeatures(); // 初始化虹膜特征值集合 + void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 QVector getFaceFeatures(); FaceCameraController * faceCam; - FaceDetectRegistProcess * faceRegistPro; + FaceRegistProcess * faceRegistPro; FaceDetectRecogProcess * faceRecogPro; + IrisCameraController * irisCam; + IrisRegistProcess * irisRegistPro; + private: ProMemory(); QStack faceQueue; // 人脸信息队列 -// QStack irisQueue; // 虹膜信息队列 + QStack irisQueue; // 虹膜信息队列 // QStack irisLeftQueue; // 左眼信息队列 // QStack irisRightQueue; // 右眼信息队列 diff --git a/casic/casic.pri b/casic/casic.pri index 852152f..613391a 100644 --- a/casic/casic.pri +++ b/casic/casic.pri @@ -1,4 +1,5 @@ include(face/casicFace.pri) +include(iris/casicIris.pri) HEADERS += $$PWD/CasicBioRecConst.h HEADERS += $$PWD/ProMemory.h diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..a3a79f7 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,23 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + QString leftOrRight = ""; // 标识左眼或者右眼 + cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) + + // 眼部图像 + // 后续计算需要使用 + QImage data; + cv::Mat matData; + + // 字节数组形式的特征码 + QByteArray irisCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..0b861ec --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,130 @@ +#include "CasicIrisInterface.h" + +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + +} + +casic::iris::CasicIrisInterface::~CasicIrisInterface() +{ + +} + +void casic::iris::CasicIrisInterface::setCascadeFile(QString filename) +{ + this->cascadeName = filename.toStdString(); +} +void casic::iris::CasicIrisInterface::setMinEyeSize(int minEyeSize) +{ + this->minEyeSize = minEyeSize; +} +void casic::iris::CasicIrisInterface::setMaxEyeSize(int maxEyeSize) +{ + this->maxEyeSize = maxEyeSize; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + QElapsedTimer timer; + timer.start(); + + std::vector rect; + cv::Size minEyeRectSize(minEyeSize, minEyeSize); + cv::Size maxEyeRectSize(maxEyeSize, maxEyeSize); + + // ★分类器对象调用 + cascade->detectMultiScale(irisInfo.matData, rect, 1.1, 3, cv::CASCADE_FIND_BIGGEST_OBJECT, minEyeRectSize, maxEyeRectSize); + + if (rect.size() == 0) + { + irisInfo.hasEye = false; + return irisInfo; + } + + LOG(DEBUG) << QString("眼睛检测算法[tm: %1 ms][count: %2][rect: (%3, %4) %5 * %6]") + .arg(timer.elapsed()).arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height).toStdString(); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvRound(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvRound(rect.at(0).y + rect.at(0).height*0.5); + + // 限制中心点的范围 + if (center.x < SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) + { + // x小于240则x=240 + center.x = center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } else if (center.x > SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5) { + // x大于1040则x=1040 + center.x = SettingConfig::getInstance().IRIS_FRAME_WIDTH - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5; + } + + if (center.y < SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) + { + // y小于180则y=180 + center.y = SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } else if (center.y > SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5) { + center.y = SettingConfig::getInstance().IRIS_FRAME_HEIGHT - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5; + } + + // 裁剪眼部图像 + cv::Mat halfSize = irisInfo.matData.clone(); + halfSize = irisInfo.matData(cv::Rect(center.x - SettingConfig::getInstance().IRIS_WIDTH * cutRatio * 0.5, + center.y - SettingConfig::getInstance().IRIS_HEIGHT * cutRatio * 0.5, + SettingConfig::getInstance().IRIS_WIDTH * cutRatio, + SettingConfig::getInstance().IRIS_HEIGHT * cutRatio)); + + cv::resize(halfSize, halfSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(halfSize, halfSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.eyeRect = rect.at(0); + + irisInfo.matData = halfSize; + irisInfo.data = ImageUtil::MatImageToQImage(halfSize); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisCodeExtract(CasicIrisInfo irisInfo) +{ + + QElapsedTimer timer; + timer.start(); + +// QSocketClientUtil * clientUtil = new QSocketClientUtil(); + + QByteArray data((char*)irisInfo.matData.data, 640*480); + + std::cout << "extract iris code" << std::endl; + +// clientUtil->sendData(data); +// if (response.size() == 1024) +// { +// QLog::getInstance()->logger()->debug(QString("特征值提取[耗时: %1ms][特征值: %2]\n") +// .arg(timer.elapsed()) +// .arg(QByteUtil::binToHexString(response))); +// } else +// { +// std::cout << response.toStdString() << std::endl; +// } + + return irisInfo; +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..50801e8 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,44 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +namespace casic { + namespace iris { + class CasicIrisInterface + { + public: + ~CasicIrisInterface(); + CasicIrisInterface(const CasicIrisInterface&)=delete; + CasicIrisInterface& operator=(const CasicIrisInterface&)=delete; + + static CasicIrisInterface& getInstance() { + static CasicIrisInterface instance; + return instance; + } + + CasicIrisInfo findEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.75; + + cv::CascadeClassifier * cascade; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..d0942ce --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +SOURCES += $$PWD/CasicIrisInterface.cpp diff --git a/dao/FaceDataDao.cpp b/dao/FaceDataDao.cpp index 7d1f873..582056d 100644 --- a/dao/FaceDataDao.cpp +++ b/dao/FaceDataDao.cpp @@ -138,14 +138,14 @@ query.prepare(sql); query.bindValue(":face", object.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -173,14 +173,14 @@ query.prepare(sql); query.bindValue(":faceCode", newObject.value("face_code")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(); + LOG(DEBUG) << QString("编辑人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -196,14 +196,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/FaceDataImgDao.cpp b/dao/FaceDataImgDao.cpp index 7f3704e..ab92be8 100644 --- a/dao/FaceDataImgDao.cpp +++ b/dao/FaceDataImgDao.cpp @@ -146,7 +146,7 @@ // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("保存人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 返回结果 if (success == true) @@ -179,7 +179,7 @@ // 执行更新 bool success = query.exec(); - LOG(DEBUG) << sql.toStdString(); + LOG(DEBUG) << QString("编辑人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -197,14 +197,14 @@ // DELETE QString sql = QString("DELETE FROM FACE_DATA_IMAGE WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 bool success = query.exec(sql); + LOG(DEBUG) << QString("删除人脸图片[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp index 5f1b047..58e2591 100644 --- a/dao/IrisDataDao.cpp +++ b/dao/IrisDataDao.cpp @@ -23,8 +23,8 @@ while (query.next()) { QVariantMap item; - item.insert("id", query.value("id").toLongLong()); - item.insert("person_id", query.value("person_id").toLongLong()); + item.insert("id", query.value("id").toString()); + item.insert("person_id", query.value("person_id").toString()); item.insert("id_card_no", query.value("id_card_no").toString()); item.insert("left_iris_code1", query.value("left_iris_code1")); item.insert("left_iris_code2", query.value("left_iris_code2")); @@ -42,8 +42,65 @@ QVariantMap IrisDataDao::findRecordById(QString id) { - QVariantMap item; - return item; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = '%1'").arg(id); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + // 获取结果 + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(DEBUG) << QString("根据id查询IRIS_DATA表的记录[%1][%2]").arg(result.size()).arg(sql).toStdString(); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = '%1'").arg(personId); + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVariantMap result; + + if (query.next()) + { + result.insert("id", query.value("id").toString()); + result.insert("person_id", query.value("person_id").toLongLong()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("left_iris_code1", query.value("left_iris_code1")); + result.insert("left_iris_code2", query.value("left_iris_code2")); + result.insert("left_iris_code3", query.value("left_iris_code3")); + result.insert("right_iris_code1", query.value("right_iris_code1")); + result.insert("right_iris_code2", query.value("right_iris_code2")); + result.insert("right_iris_code3", query.value("right_iris_code3")); + } + + LOG(TRACE) << QString("根据personId查询IRIS_DATA表的记录[personId=%1][%2]").arg(personId).arg(sql).toStdString(); + return result; } @@ -73,14 +130,14 @@ query.bindValue(":left1", object.value("left_iris_code1")); query.bindValue(":right1", object.value("right_iris_code1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -97,7 +154,31 @@ bool IrisDataDao::edit(QVariantMap newObject, QString id) { - return false; + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // UPDATE语句 + if (!newObject.contains("left_iris_code1") || !newObject.contains("right_iris_code1")){ + return false; + } + QString sql = QString("UPDATE IRIS_DATA SET LEFT_IRIS_CODE1 = :left1, RIGHT_IRIS_CODE1 = :right1 WHERE ID = '%1'").arg(id); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_iris_code1")); + query.bindValue(":right1", newObject.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜特征值[%1][%2]").arg(success).arg(sql).toStdString(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; } diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h index 1289074..ec6c658 100644 --- a/dao/IrisDataDao.h +++ b/dao/IrisDataDao.h @@ -12,6 +12,7 @@ QVector findAllRecord(); QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); QVector findRecordsByProperty(QString properName, QVariant properValue); QString save(QVariantMap object); diff --git a/dao/IrisDataImgDao.cpp b/dao/IrisDataImgDao.cpp index 8a0c9af..262a9e4 100644 --- a/dao/IrisDataImgDao.cpp +++ b/dao/IrisDataImgDao.cpp @@ -83,14 +83,8 @@ // 返回结果 QVariantMap result; - // 获取结果集的大小 - query.last(); - int count = query.at() + 1; - - if (count >=1) + if (query.next()) { - query.first(); - result.insert("id", query.value("id").toString()); result.insert("person_id", query.value("person_id").toString()); result.insert("left_image1", query.value("left_image1").toString()); @@ -157,13 +151,12 @@ query.bindValue(":leftImage1", object.value("left_image1")); query.bindValue(":rightImage1", object.value("right_image1")); - LOG(DEBUG) << sql.toStdString(); - // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行插入 bool success = query.exec(); + LOG(DEBUG) << QString("保存虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); @@ -184,30 +177,24 @@ // 新建查询 QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + if(!newObject.contains("left_image1") && !newObject.contains("right_image1")) { + return false; + } // UPDATE语句 - QString sql = QString("UPDATE IRIS_DATA_IMAGE SET "); - if(!newObject.contains("left_image")&&!newObject.contains("right_image")){ - return true; - } - if (newObject.contains("left_image")) - { - sql.append(QString("LEFT_IMAGE1 = '%1', ").arg(newObject.value("left_image").toString())); - } - if (newObject.contains("right_image")) - { - sql.append(QString("right_image1 = '%1', ").arg(newObject.value("right_image").toString())); - } - sql.remove(sql.length()-2,2); - sql.append(QString(" WHERE PERSON_ID = %1").arg(id)); + QString sql = QString("UPDATE IRIS_DATA_IMAGE SET LEFT_IMAGE1 = :left1, RIGHT_IMAGE1 = :right1 WHERE ID = '%1'").arg(id); - LOG(DEBUG) << sql.toStdString(); + query.prepare(sql); + query.bindValue(":left1", newObject.value("left_image1")); + query.bindValue(":right1", newObject.value("right_image1")); // 开启事务 ConnectionManager::getInstance()->getConnection().transaction(); // 执行更新 - bool success = query.exec(sql); + bool success = query.exec(); + + LOG(DEBUG) << QString("编辑虹膜图片[%1][%2]").arg(success).arg(sql).toStdString(); // 结束事务 ConnectionManager::getInstance()->getConnection().commit(); diff --git a/device/FaceCameraController.cpp b/device/FaceCameraController.cpp index 5cb98e5..25854c3 100644 --- a/device/FaceCameraController.cpp +++ b/device/FaceCameraController.cpp @@ -1,8 +1,5 @@ #include "FaceCameraController.h" -#include -#include -#include "CasicBioRecWin.h" FaceCameraController::FaceCameraController(QObject *parent) : QObject(parent) { diff --git a/device/FaceCameraController.h b/device/FaceCameraController.h index b6dfd61..68cd26a 100644 --- a/device/FaceCameraController.h +++ b/device/FaceCameraController.h @@ -6,10 +6,7 @@ #include "opencv2/opencv.hpp" #include "casic/ProMemory.h" -#include "utils/ImageUtil.h" -#include "utils/SettingConfig.h" -#include "utils/TimeCounterUtil.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class FaceCameraController : public QObject { diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..da744f5 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,75 @@ +#include "IrisCameraCapEventHandler.h" + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + size_t leftKeyIndex = camName.find("left"); + size_t rightKeyIndex = camName.find("right"); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + + if (pBuffer != NULL) + { + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + // 左眼相机拍摄的图像 + irisInfo.leftOrRight = "left"; + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + // 右眼相机 + irisInfo.leftOrRight = "right"; + } + + if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::MAIN_PAGE) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage imgDisp = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + cv::Mat irisMat = ImageUtil::QImageToMat(imgDisp); + + irisInfo.data = imgDisp; + irisInfo.matData = irisMat; + + // 识别过程任意一个眼睛都行, 全部存入左眼队列 + ProMemory::getInstance().pushCasicIris(irisInfo); + } else if (ProMemory::getInstance().widgeFrame == CasicBioRecConst::ADD_PERSON_CAPTURE_IRIS) + { + // 相机拍摄下的图像1280*960, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); + QImage imgAlgo = img.copy(0, 0, width, height); + + // 用于显示的图像需要缩小到1/4的尺寸 + img = img.scaled(640, 480); + cv::Mat irisMat = ImageUtil::QImageToMat(imgAlgo); + + irisInfo.data = imgAlgo; + irisInfo.matData = irisMat; + + // 将图像显示在注册页面的label上 + // 并将虹膜信息对象存入队列中 + ProMemory::getInstance().pushCasicIris(irisInfo); + + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 0); // 左眼画面 + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + emit sendIrisFrameToDraw(img, 1); // 右眼画面 + } + } + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..f778692 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,30 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include "opencv2/opencv.hpp" +#include "daheng/include/GalaxyIncludes.h" + +#include "casic/iris/CasicIrisInterface.h" +#include "casic/ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage, int leftOrRight); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..ed9f890 --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,159 @@ +#include "IrisCameraController.h" + +static qint8 IRIS_CAMERA_SWITCHER = 0; // 用于切换左右两个相机工作的奇偶变量 + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + this->leftHandler = new IrisCameraCapEventHandler(this); + this->rightHandler = new IrisCameraCapEventHandler(this); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCamera() +{ + IGXFactory::GetInstance().Init(); + + //枚举设备 + IGXFactory::GetInstance().UpdateDeviceList(1000, irisCamList); + + this->OpenDevice(); +} + +void IrisCameraController::openIrisCamera() +{ + //设置Buffer处理模式 + leftStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + rightStreamFeaturePtr->GetEnumFeature("StreamBufferHandlingMode")->SetValue("OldestFirst"); + + //注册回调函数 + leftStreamPtr->RegisterCaptureCallback(leftHandler, &leftCamPtr); + rightStreamPtr->RegisterCaptureCallback(rightHandler, &rightCamPtr); + + //开启流层通道 + leftStreamPtr->StartGrab(); + rightStreamPtr->StartGrab(); + + //发送开采命令 + leftFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + rightFeaturePtr->GetCommandFeature("AcquisitionStart")->Execute(); + + // 启动定时器 + TimeCounterUtil::getInstance().irisCapCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); // 50ms执行一次定时器 +} + +void IrisCameraController::closeIrisCamera() +{ + +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 + connect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString(); +} +void IrisCameraController::stopCapture() +{ + // 获取定时器, 绑定定时函数 + disconnect(TimeCounterUtil::getInstance().irisCapCounter, &QTimer::timeout, + this, &IrisCameraController::getOneFaceFrm); + LOG(DEBUG) << QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString(); +} + +void IrisCameraController::getOneFaceFrm() +{ + // 发送软触发命令(在触发模式开启时有效) + if (IRIS_CAMERA_SWITCHER % 2 == 0) + { + leftFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } else + { + rightFeaturePtr->GetCommandFeature("TriggerSoftware")->Execute(); + } + + IRIS_CAMERA_SWITCHER++; +} + +void IrisCameraController::OpenDevice() +{ + for(size_t i = 0; i < this->irisCamList.size(); i++) + { + auto device = irisCamList.at(i); + size_t leftKeyIndex = device.GetUserID().find("left"); + size_t rightKeyIndex = device.GetUserID().find("right"); + if (leftKeyIndex >= 0 && leftKeyIndex < 32) + { + leftCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + leftFeaturePtr = leftCamPtr->GetRemoteFeatureControl(); + + // 判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = leftCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + leftStreamPtr = leftCamPtr->OpenStream(0); + leftStreamFeaturePtr = leftStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = leftCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == leftFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = leftStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + leftFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + leftFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + leftFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + leftFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } else if (rightKeyIndex >= 0 && rightKeyIndex < 32) + { + rightCamPtr = IGXFactory::GetInstance().OpenDeviceByUserID(device.GetUserID(), GX_ACCESS_EXCLUSIVE); + rightFeaturePtr = rightCamPtr->GetRemoteFeatureControl(); + + //判断设备流是否大于零, 如果大于零则打开流 + int nStreamCount = rightCamPtr->GetStreamCount(); + if (nStreamCount > 0) + { + rightStreamPtr = rightCamPtr->OpenStream(0); + rightStreamFeaturePtr = rightStreamPtr->GetFeatureControl(); + } + + // 建议用户在打开网络相机之后, 根据当前网络环境设置相机的流通道包长值, + // 以提高网络相机的采集性能, 设置方法参考以下代码。 + GX_DEVICE_CLASS_LIST objDeviceClass = rightCamPtr->GetDeviceInfo().GetDeviceClass(); + if(GX_DEVICE_CLASS_GEV == objDeviceClass) + { + // 判断设备是否支持流通道数据包功能 + if(true == rightFeaturePtr->IsImplemented("GevSCPSPacketSize")) + { + // 获取当前网络环境的最优包长值 + int nPacketSize = rightStreamPtr->GetOptimalPacketSize(); + // 将最优包长值设置为当前设备的流通道包长值 + rightFeaturePtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize); + } + } + + //设置采集模式为连续采集模式 + rightFeaturePtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous"); + + //设置触发模式关 + rightFeaturePtr->GetEnumFeature("TriggerMode")->SetValue("On"); + rightFeaturePtr->GetEnumFeature("TriggerSource")->SetValue("Software"); + } + } +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..744a68d --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,52 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "IrisCameraCapEventHandler.h" + +class IrisCameraCapEventHandler; + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + // 初始化并打开人脸相机 + void initIrisCamera(); + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + IrisCameraCapEventHandler * leftHandler; + IrisCameraCapEventHandler * rightHandler; + +private: + GxIAPICPP::gxdeviceinfo_vector irisCamList; + + CGXDevicePointer leftCamPtr; ///< 左眼设备句柄 + CGXDevicePointer rightCamPtr; ///< 右眼设备句柄 + + CGXStreamPointer leftStreamPtr; ///< 左眼设备流 + CGXStreamPointer rightStreamPtr; ///< 左眼设备流 + + CGXFeatureControlPointer leftFeaturePtr; ///< 左眼属性控制器 + CGXFeatureControlPointer rightFeaturePtr; ///< 右眼属性控制器 + + CGXFeatureControlPointer leftStreamFeaturePtr; ///< 左眼流层控制器对象 + CGXFeatureControlPointer rightStreamFeaturePtr; ///< 右眼流层控制器对象 + + void OpenDevice(); + +signals: + +public slots: + void getOneFaceFrm(); + +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri index 274ba60..6dc2f3c 100644 --- a/device/device.pri +++ b/device/device.pri @@ -1,20 +1,24 @@ -HEADERS += $$PWD/FaceCameraController.h -HEADERS += $$PWD/face/FaceDetectRegistProcess.h +HEADERS += $$PWD/FaceCameraController.h \ + $$PWD/face/FaceRegistProcess.h +HEADERS += HEADERS += $$PWD/face/FaceDetectRecogProcess.h HEADERS += $$PWD/face/CasicFaceRecState.h -SOURCES += $$PWD/FaceCameraController.cpp -SOURCES += $$PWD/face/FaceDetectRegistProcess.cpp +SOURCES += $$PWD/FaceCameraController.cpp \ + $$PWD/face/FaceRegistProcess.cpp +SOURCES += SOURCES += $$PWD/face/FaceDetectRecogProcess.cpp SOURCES += $$PWD/face/CasicFaceRecState.cpp -#HEADERS += $$PWD/IrisCameraController.h -#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/IrisCameraController.h +HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/iris/IrisRegistProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h #HEADERS += $$PWD/MotoController.h -#HEADERS += $$PWD/DeviceEnumerator.h -#SOURCES += $$PWD/IrisCameraController.cpp -#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/IrisCameraController.cpp +SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/iris/IrisRegistProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp #SOURCES += $$PWD/MotoController.cpp -#SOURCES += $$PWD/DeviceEnumerator.cpp diff --git a/device/face/CasicFaceRecState.h b/device/face/CasicFaceRecState.h index f259312..8eac2bd 100644 --- a/device/face/CasicFaceRecState.h +++ b/device/face/CasicFaceRecState.h @@ -8,7 +8,7 @@ #include #include "casic/face/CasicFaceInfo.h" -#include "utils/easyloggingpp/easylogging++.h" +#include "utils/UtilInclude.h" class CasicFaceRecState : public QObject { diff --git a/device/face/FaceDetectRecogProcess.h b/device/face/FaceDetectRecogProcess.h index b1051b7..4ca4e94 100644 --- a/device/face/FaceDetectRecogProcess.h +++ b/device/face/FaceDetectRecogProcess.h @@ -7,14 +7,10 @@ #include "dao/RecognitionRecordDao.h" -#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" +#include "utils/UtilInclude.h" class FaceDetectRecogProcess : public QThread { diff --git a/device/face/FaceDetectRegistProcess.cpp b/device/face/FaceDetectRegistProcess.cpp deleted file mode 100644 index ba078cc..0000000 --- a/device/face/FaceDetectRegistProcess.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "FaceDetectRegistProcess.h" - -FaceDetectRegistProcess::FaceDetectRegistProcess(QObject *parent) : QThread(parent) -{ - this->working = false; - this->exit = false; -} - -FaceDetectRegistProcess::~FaceDetectRegistProcess() -{ - 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 deleted file mode 100644 index 99a504c..0000000 --- a/device/face/FaceDetectRegistProcess.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef FACEDETECTREGISTPROCESS_H -#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" - -class FaceDetectRegistProcess : public QThread -{ - Q_OBJECT -public: - explicit FaceDetectRegistProcess(QObject *parent = nullptr); - ~FaceDetectRegistProcess(); - - 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 failedCaptureFace(); - void successCaptureFace(QString personIdByFace); - -}; - -#endif // FACEDETECTREGISTPROCESS_H diff --git a/device/face/FaceRegistProcess.cpp b/device/face/FaceRegistProcess.cpp new file mode 100644 index 0000000..235b855 --- /dev/null +++ b/device/face/FaceRegistProcess.cpp @@ -0,0 +1,208 @@ +#include "FaceRegistProcess.h" + +FaceRegistProcess::FaceRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; +} + +FaceRegistProcess::~FaceRegistProcess() +{ + this->setWorking(false); +} + +void FaceRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void FaceRegistProcess::exitThread() +{ + this->exit = true; +} + +void FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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 FaceRegistProcess::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/FaceRegistProcess.h b/device/face/FaceRegistProcess.h new file mode 100644 index 0000000..aa45763 --- /dev/null +++ b/device/face/FaceRegistProcess.h @@ -0,0 +1,42 @@ +#ifndef FACEREGISTPROCESS_H +#define FACEREGISTPROCESS_H + +#include +#include +#include "opencv2/opencv.hpp" + +#include "CasicFaceRecState.h" +#include "casic/ProMemory.h" +#include "casic/face/CasicFaceInterface.h" +#include "utils/UtilInclude.h" + +class FaceRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit FaceRegistProcess(QObject *parent = nullptr); + ~FaceRegistProcess(); + + 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 failedCaptureFace(); + void successCaptureFace(QString personIdByFace); + +}; + +#endif // FACEREGISTPROCESS_H diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..28625a1 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,61 @@ +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz").toStdString(); + this->timeStamp = now.toMSecsSinceEpoch(); + this->tryCount = 0; + this->noEyeCount = 0; + + LOG(DEBUG) << QString("[CasicIrisRecState][initRecognize] 虹膜识别状态初始化").toStdString(); +} + +std::string CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)).toStdString(); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", QString::fromStdString(recoginzeId)); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + obj.insert("state", state); + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + obj.insert("recogTimeLast", recogTimeLast); + + QJsonObject eyeObj; + eyeObj.insert("hasEye", irisInfo->hasEye); + eyeObj.insert("leftOrRight", irisInfo->leftOrRight); + QJsonArray rectArray; + rectArray.append(irisInfo->eyeRect.x); + rectArray.append(irisInfo->eyeRect.y); + rectArray.append(irisInfo->eyeRect.width); + rectArray.append(irisInfo->eyeRect.height); + eyeObj.insert("faceRect", rectArray); + + obj.insert("eye", eyeObj); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..43bf234 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,67 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + std::string toString(); + QJsonObject toJSON(); + + std::string recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + + CasicIrisInfo * irisInfo; + + qint8 tryCount = 0; // 识别尝试次数 + qint8 noEyeCount = 0; // 连续没有找到眼睛次数 + float recogTimeLast = 0.0; // 识别成功耗时 + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_LEFT_FIND_EYE = 1, // 检测左眼, 没找到右眼 + REC_RIGHT_FIND_EYE = 2, // 检测右眼, 没找到左眼 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRegistProcess.cpp b/device/iris/IrisRegistProcess.cpp new file mode 100644 index 0000000..7696cf3 --- /dev/null +++ b/device/iris/IrisRegistProcess.cpp @@ -0,0 +1,180 @@ +#include "IrisRegistProcess.h" + +IrisRegistProcess::IrisRegistProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect("127.0.0.1", 2015); + + // 调用socket发送消息 + connect(this, &IrisRegistProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRegistProcess::~IrisRegistProcess() +{ + this->setWorking(false); +} + +void IrisRegistProcess::setWorking(bool working) +{ + this->working = working; +} +void IrisRegistProcess::exitThread() +{ + this->exit = true; +} + +void IrisRegistProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; + LOG(DEBUG) << QString("[CasicIrisRegistProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); + + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + emit captureIrisFailure(); + } else { + ProMemory::getInstance().irisRegistPro->setWorking(true); // 继续工作 + } +} +void IrisRegistProcess::addOneNoEyeCount() +{ + CasicIrisRecState::getInstance().noEyeCount++; + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + emit captureIrisFailure(); + } +} + +void IrisRegistProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(200); // 200ms后再判断 + continue; + } + + // 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + LOG(DEBUG) << QString("[CasicIrisRegistThread]虹膜图像栈空 暂停200ms").toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 取出一幅图像 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + LOG(DEBUG) << QString("[CasicIrisRegistThread]取出一幅虹膜图").toStdString(); + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_LEFT_FIND_EYE && + irisInfo.leftOrRight == "left") + { + // 左眼已经编码成功则不再判断左眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]左眼已经编码成功则不再判断左眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } else if (CasicIrisRecState::getInstance().state == CasicIrisRecState::IrisRecStateName::REC_RIGHT_FIND_EYE && + irisInfo.leftOrRight == "right"){ + // 右眼已经编码成功则不再判断右眼 + LOG(DEBUG) << QString("[CasicIrisRegistThread]右眼已经编码成功则不再判断右眼[%1][%2]").arg(CasicIrisRecState::getInstance().state).arg(irisInfo.leftOrRight).toStdString(); + this->msleep(200); // 200ms后再判断 + continue; + } + + // 调用找眼分类器算法 + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + + // 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + LOG(DEBUG) << QString("[CasicIrisRegistThread]分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount).toStdString(); + addOneNoEyeCount(); + + // 双眼都要注册, 没找到眼不重置状态 + this->msleep(200); // 200ms后再判断 + continue; + } + + // 找到眼睛则暂停线程工作 + ProMemory::getInstance().irisRegistPro->setWorking(false); + LOG(DEBUG) << QString("找到眼睛暂停线程工作").toStdString(); + + // 找到眼睛则开始质量评估和编码 + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; + CasicIrisRecState::getInstance().irisInfo->leftOrRight = irisInfo.leftOrRight; + CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; + CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; + CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + + QElapsedTimer timer; + timer.start(); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); + data.prepend("[-r]"); // 注册编码 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + QByteArray encodeResponse = clientUtil->getResponse(); + + // 清除队列缓存 + ProMemory::getInstance().clearIrisQueue(); + + // 编码成功 + if (encodeResponse.size() == 1024) + { + LOG(DEBUG) << QString("虹膜特征值提取[耗时%1ms][%2]").arg(timer.elapsed()).arg(irisInfo.leftOrRight).toStdString(); + + // 保存图像文件 + cv::imwrite(QString("D:\\irisLogs\\%1-%2.bmp").arg(QDateTime::currentDateTime().toString("HHmmsszzz")).arg(irisInfo.leftOrRight).toStdString(), irisInfo.matData); + + CasicIrisRecState::getInstance().irisInfo->irisCode = encodeResponse; + + // 编码成功 到业务中判断 + emit findNoSimIris(CasicIrisRecState::getInstance().irisInfo); + } else if (encodeResponse.contains("-100") == true) + { + // 编码失败 + addOneTryCount(); + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + } else if (encodeResponse.contains("-101") == true || + encodeResponse.contains("-102") == true) + { + // 找到匹配的人 + QString message = encodeResponse.mid(5, encodeResponse.size() - 6); + QStringList messList = message.split(";"); + QString personId = messList.at(1); + + // 到业务中进行判断 + emit findMatchedIris(personId); + } else + { + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + // 超时返回则尝试次数+1 + addOneTryCount(); + } + } +} diff --git a/device/iris/IrisRegistProcess.h b/device/iris/IrisRegistProcess.h new file mode 100644 index 0000000..5533387 --- /dev/null +++ b/device/iris/IrisRegistProcess.h @@ -0,0 +1,45 @@ +#ifndef IRISREGISTPROCESS_H +#define IRISREGISTPROCESS_H + +#include +#include +#include +#include + +#include "casic/ProMemory.h" +#include "device/iris/CasicIrisRecState.h" + +class IrisRegistProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRegistProcess(QObject *parent = nullptr); + ~IrisRegistProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + +signals: + void sendDataToExract(QByteArray data); + + void findNoSimIris(CasicIrisInfo * irisInfo); + void findMatchedIris(QString personId); + void captureIrisFailure(); +}; + +#endif // IRISREGISTPROCESS_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp index 3a4c678..3d565fa 100644 --- a/utils/ImageUtil.cpp +++ b/utils/ImageUtil.cpp @@ -43,7 +43,7 @@ //在不改变实际图像数据的条件下, 交换红蓝通道 return qImage.rgbSwapped(); } - +*/ cv::Mat ImageUtil::QImageToMat(QImage image) { cv::Mat mat; @@ -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(); diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h index 5ef3c13..731fc48 100644 --- a/utils/ImageUtil.h +++ b/utils/ImageUtil.h @@ -13,8 +13,8 @@ static QImage MatImageToQImage(const cv::Mat &src); // static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); -// static cv::Mat QImageToMat(QImage image); -// static QString QImageToBase64(QImage image); + static cv::Mat QImageToMat(QImage image); + static QString QImageToBase64(QImage image); static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); }; diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..d1ef79a --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,34 @@ +#include "SocketClientUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, + this, &SocketClientUtil::readData); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::sendData(QByteArray data) +{ + data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + + this->response = buffer; + emit this->responseReaded(); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..c3cf6e4 --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,33 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + QByteArray getResponse(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/UtilInclude.h b/utils/UtilInclude.h index f3ded58..6019de0 100644 --- a/utils/UtilInclude.h +++ b/utils/UtilInclude.h @@ -8,5 +8,6 @@ #include "SettingConfig.h" #include "SpeakerUtil.h" #include "TimeCounterUtil.h" +#include "SocketClientUtil.h" #endif // UTILINCLUDE_H diff --git a/utils/utils.pri b/utils/utils.pri index 54ecc1c..b3d3d63 100644 --- a/utils/utils.pri +++ b/utils/utils.pri @@ -27,13 +27,7 @@ HEADERS += $$PWD/ByteUtil.h SOURCES += $$PWD/ByteUtil.cpp -#HEADERS += $$PWD/QSocketClientUtil.h -#HEADERS += $$PWD/TimerCounter.h - - - - -#SOURCES += $$PWD/QSocketClientUtil.cpp -#SOURCES += $$PWD/TimerCounter.cpp +HEADERS += $$PWD/SocketClientUtil.h +SOURCES += $$PWD/SocketClientUtil.cpp