diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/dao/dao.pri b/dao/dao.pri new file mode 100644 index 0000000..300225e --- /dev/null +++ b/dao/dao.pri @@ -0,0 +1,20 @@ + +HEADERS += $$PWD/BaseDao.h +HEADERS += $$PWD/IrisDataDao.h +HEADERS += $$PWD/RecognitionRecordsDao.h +HEADERS += $$PWD/SysPersonDao.h +HEADERS += $$PWD/SysDeptDao.h +HEADERS += $$PWD/SysDictDao.h + +SOURCES += $$PWD/BaseDao.cpp +SOURCES += $$PWD/IrisDataDao.cpp +SOURCES += $$PWD/RecognitionRecordsDao.cpp +SOURCES += $$PWD/SysPersonDao.cpp +SOURCES += $$PWD/SysDeptDao.cpp +SOURCES += $$PWD/SysDictDao.cpp + +HEADERS += $$PWD/util/ConnectionManager.h +SOURCES += $$PWD/util/ConnectionManager.cpp +#HEADERS += $$PWD/util/CacheManager.h +#SOURCES += $$PWD/util/CacheManager.cpp + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/dao/dao.pri b/dao/dao.pri new file mode 100644 index 0000000..300225e --- /dev/null +++ b/dao/dao.pri @@ -0,0 +1,20 @@ + +HEADERS += $$PWD/BaseDao.h +HEADERS += $$PWD/IrisDataDao.h +HEADERS += $$PWD/RecognitionRecordsDao.h +HEADERS += $$PWD/SysPersonDao.h +HEADERS += $$PWD/SysDeptDao.h +HEADERS += $$PWD/SysDictDao.h + +SOURCES += $$PWD/BaseDao.cpp +SOURCES += $$PWD/IrisDataDao.cpp +SOURCES += $$PWD/RecognitionRecordsDao.cpp +SOURCES += $$PWD/SysPersonDao.cpp +SOURCES += $$PWD/SysDeptDao.cpp +SOURCES += $$PWD/SysDictDao.cpp + +HEADERS += $$PWD/util/ConnectionManager.h +SOURCES += $$PWD/util/ConnectionManager.cpp +#HEADERS += $$PWD/util/CacheManager.h +#SOURCES += $$PWD/util/CacheManager.cpp + diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..dfbf26d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,54 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#include +#include + +Q_GLOBAL_STATIC(ConnectionManager, cm) + +ConnectionManager::ConnectionManager(QObject *parent) : QObject(parent) +{ + // 初始化数据库连接 + if (QSqlDatabase::contains("qt_sql_dafault_connection")) + { + conn = QSqlDatabase::database("qt_sql_default_connection"); + } + else + { + conn = QSqlDatabase::addDatabase("QSQLITE"); + conn.setDatabaseName(QApplication::applicationDirPath() + "/data/casic.db"); + + bool succ = conn.open(); + if (succ == true) + { +// LOG_INFO(QString("打开数据库操作正常[Open Database Success]").toStdString()); + qDebug() << "打开数据库操作正常[Open Database Success]"; + } else + { +// LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + qDebug() << "打开数据库操作失败[Open Database Failed]"; + } + } +} + +ConnectionManager::~ConnectionManager() +{ + conn.close(); +} + +ConnectionManager* ConnectionManager::getInstance() +{ + return cm; +} + +QSqlDatabase ConnectionManager::getConnection() +{ + return this->conn; +} + +qint64 ConnectionManager::generateId() +{ + return this->idWorker.nextId(); +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/dao/dao.pri b/dao/dao.pri new file mode 100644 index 0000000..300225e --- /dev/null +++ b/dao/dao.pri @@ -0,0 +1,20 @@ + +HEADERS += $$PWD/BaseDao.h +HEADERS += $$PWD/IrisDataDao.h +HEADERS += $$PWD/RecognitionRecordsDao.h +HEADERS += $$PWD/SysPersonDao.h +HEADERS += $$PWD/SysDeptDao.h +HEADERS += $$PWD/SysDictDao.h + +SOURCES += $$PWD/BaseDao.cpp +SOURCES += $$PWD/IrisDataDao.cpp +SOURCES += $$PWD/RecognitionRecordsDao.cpp +SOURCES += $$PWD/SysPersonDao.cpp +SOURCES += $$PWD/SysDeptDao.cpp +SOURCES += $$PWD/SysDictDao.cpp + +HEADERS += $$PWD/util/ConnectionManager.h +SOURCES += $$PWD/util/ConnectionManager.cpp +#HEADERS += $$PWD/util/CacheManager.h +#SOURCES += $$PWD/util/CacheManager.cpp + diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..dfbf26d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,54 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#include +#include + +Q_GLOBAL_STATIC(ConnectionManager, cm) + +ConnectionManager::ConnectionManager(QObject *parent) : QObject(parent) +{ + // 初始化数据库连接 + if (QSqlDatabase::contains("qt_sql_dafault_connection")) + { + conn = QSqlDatabase::database("qt_sql_default_connection"); + } + else + { + conn = QSqlDatabase::addDatabase("QSQLITE"); + conn.setDatabaseName(QApplication::applicationDirPath() + "/data/casic.db"); + + bool succ = conn.open(); + if (succ == true) + { +// LOG_INFO(QString("打开数据库操作正常[Open Database Success]").toStdString()); + qDebug() << "打开数据库操作正常[Open Database Success]"; + } else + { +// LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + qDebug() << "打开数据库操作失败[Open Database Failed]"; + } + } +} + +ConnectionManager::~ConnectionManager() +{ + conn.close(); +} + +ConnectionManager* ConnectionManager::getInstance() +{ + return cm; +} + +QSqlDatabase ConnectionManager::getConnection() +{ + return this->conn; +} + +qint64 ConnectionManager::generateId() +{ + return this->idWorker.nextId(); +} diff --git a/dao/util/ConnectionManager.h b/dao/util/ConnectionManager.h new file mode 100644 index 0000000..3121d9a --- /dev/null +++ b/dao/util/ConnectionManager.h @@ -0,0 +1,34 @@ +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include +#include +#include "utils/id/IdWorker.h" +//#include "utils/UtilInclude.h" + +using namespace Jiawa::Core; + +class ConnectionManager : public QObject +{ + Q_OBJECT +public: + explicit ConnectionManager(QObject *parent = nullptr); + ~ConnectionManager(); + + static ConnectionManager * getInstance(); + + QSqlDatabase getConnection(); + qint64 generateId(); + +private: + // 数据库连接 + QSqlDatabase conn; + + // 雪花id生成工具 + IdWorker &idWorker = Singleton::instance(); + +signals: + +}; + +#endif // CONNECTIONMANAGER_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/dao/dao.pri b/dao/dao.pri new file mode 100644 index 0000000..300225e --- /dev/null +++ b/dao/dao.pri @@ -0,0 +1,20 @@ + +HEADERS += $$PWD/BaseDao.h +HEADERS += $$PWD/IrisDataDao.h +HEADERS += $$PWD/RecognitionRecordsDao.h +HEADERS += $$PWD/SysPersonDao.h +HEADERS += $$PWD/SysDeptDao.h +HEADERS += $$PWD/SysDictDao.h + +SOURCES += $$PWD/BaseDao.cpp +SOURCES += $$PWD/IrisDataDao.cpp +SOURCES += $$PWD/RecognitionRecordsDao.cpp +SOURCES += $$PWD/SysPersonDao.cpp +SOURCES += $$PWD/SysDeptDao.cpp +SOURCES += $$PWD/SysDictDao.cpp + +HEADERS += $$PWD/util/ConnectionManager.h +SOURCES += $$PWD/util/ConnectionManager.cpp +#HEADERS += $$PWD/util/CacheManager.h +#SOURCES += $$PWD/util/CacheManager.cpp + diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..dfbf26d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,54 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#include +#include + +Q_GLOBAL_STATIC(ConnectionManager, cm) + +ConnectionManager::ConnectionManager(QObject *parent) : QObject(parent) +{ + // 初始化数据库连接 + if (QSqlDatabase::contains("qt_sql_dafault_connection")) + { + conn = QSqlDatabase::database("qt_sql_default_connection"); + } + else + { + conn = QSqlDatabase::addDatabase("QSQLITE"); + conn.setDatabaseName(QApplication::applicationDirPath() + "/data/casic.db"); + + bool succ = conn.open(); + if (succ == true) + { +// LOG_INFO(QString("打开数据库操作正常[Open Database Success]").toStdString()); + qDebug() << "打开数据库操作正常[Open Database Success]"; + } else + { +// LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + qDebug() << "打开数据库操作失败[Open Database Failed]"; + } + } +} + +ConnectionManager::~ConnectionManager() +{ + conn.close(); +} + +ConnectionManager* ConnectionManager::getInstance() +{ + return cm; +} + +QSqlDatabase ConnectionManager::getConnection() +{ + return this->conn; +} + +qint64 ConnectionManager::generateId() +{ + return this->idWorker.nextId(); +} diff --git a/dao/util/ConnectionManager.h b/dao/util/ConnectionManager.h new file mode 100644 index 0000000..3121d9a --- /dev/null +++ b/dao/util/ConnectionManager.h @@ -0,0 +1,34 @@ +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include +#include +#include "utils/id/IdWorker.h" +//#include "utils/UtilInclude.h" + +using namespace Jiawa::Core; + +class ConnectionManager : public QObject +{ + Q_OBJECT +public: + explicit ConnectionManager(QObject *parent = nullptr); + ~ConnectionManager(); + + static ConnectionManager * getInstance(); + + QSqlDatabase getConnection(); + qint64 generateId(); + +private: + // 数据库连接 + QSqlDatabase conn; + + // 雪花id生成工具 + IdWorker &idWorker = Singleton::instance(); + +signals: + +}; + +#endif // CONNECTIONMANAGER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0a0916f --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "MainWindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/dao/dao.pri b/dao/dao.pri new file mode 100644 index 0000000..300225e --- /dev/null +++ b/dao/dao.pri @@ -0,0 +1,20 @@ + +HEADERS += $$PWD/BaseDao.h +HEADERS += $$PWD/IrisDataDao.h +HEADERS += $$PWD/RecognitionRecordsDao.h +HEADERS += $$PWD/SysPersonDao.h +HEADERS += $$PWD/SysDeptDao.h +HEADERS += $$PWD/SysDictDao.h + +SOURCES += $$PWD/BaseDao.cpp +SOURCES += $$PWD/IrisDataDao.cpp +SOURCES += $$PWD/RecognitionRecordsDao.cpp +SOURCES += $$PWD/SysPersonDao.cpp +SOURCES += $$PWD/SysDeptDao.cpp +SOURCES += $$PWD/SysDictDao.cpp + +HEADERS += $$PWD/util/ConnectionManager.h +SOURCES += $$PWD/util/ConnectionManager.cpp +#HEADERS += $$PWD/util/CacheManager.h +#SOURCES += $$PWD/util/CacheManager.cpp + diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..dfbf26d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,54 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#include +#include + +Q_GLOBAL_STATIC(ConnectionManager, cm) + +ConnectionManager::ConnectionManager(QObject *parent) : QObject(parent) +{ + // 初始化数据库连接 + if (QSqlDatabase::contains("qt_sql_dafault_connection")) + { + conn = QSqlDatabase::database("qt_sql_default_connection"); + } + else + { + conn = QSqlDatabase::addDatabase("QSQLITE"); + conn.setDatabaseName(QApplication::applicationDirPath() + "/data/casic.db"); + + bool succ = conn.open(); + if (succ == true) + { +// LOG_INFO(QString("打开数据库操作正常[Open Database Success]").toStdString()); + qDebug() << "打开数据库操作正常[Open Database Success]"; + } else + { +// LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + qDebug() << "打开数据库操作失败[Open Database Failed]"; + } + } +} + +ConnectionManager::~ConnectionManager() +{ + conn.close(); +} + +ConnectionManager* ConnectionManager::getInstance() +{ + return cm; +} + +QSqlDatabase ConnectionManager::getConnection() +{ + return this->conn; +} + +qint64 ConnectionManager::generateId() +{ + return this->idWorker.nextId(); +} diff --git a/dao/util/ConnectionManager.h b/dao/util/ConnectionManager.h new file mode 100644 index 0000000..3121d9a --- /dev/null +++ b/dao/util/ConnectionManager.h @@ -0,0 +1,34 @@ +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include +#include +#include "utils/id/IdWorker.h" +//#include "utils/UtilInclude.h" + +using namespace Jiawa::Core; + +class ConnectionManager : public QObject +{ + Q_OBJECT +public: + explicit ConnectionManager(QObject *parent = nullptr); + ~ConnectionManager(); + + static ConnectionManager * getInstance(); + + QSqlDatabase getConnection(); + qint64 generateId(); + +private: + // 数据库连接 + QSqlDatabase conn; + + // 雪花id生成工具 + IdWorker &idWorker = Singleton::instance(); + +signals: + +}; + +#endif // CONNECTIONMANAGER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0a0916f --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "MainWindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..663db0f --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,43 @@ +#include "SettingConfig.h" +#include + +SettingConfig::SettingConfig() +{ + filename = QApplication::applicationDirPath() + "/conf/config.ini"; + setting = new QSettings(this->filename, QSettings::IniFormat); + + WINDOW_WIDTH = getProperty("window", "width").toInt(); + WINDOW_HEIGHT = getProperty("window", "height").toInt(); + WINDOW_BACKGROUND_COLOR = getProperty("window", "backgroundColor").toString(); + WINDOW_TITLE = getProperty("window", "title").toString(); + WINDOW_RIGHTS = getProperty("window", "copyright").toString(); + WINDOW_VERSION = getProperty("window", "version").toString(); + DEVICE_CODE = getProperty("window", "deviceCode").toString(); + + IRIS_FRAME_WIDTH = getProperty("camera", "irisFrameWidth").toInt(); + IRIS_FRAME_HEIGHT = getProperty("camera", "irisFrameHeight").toInt(); + IRIS_WIDTH = getProperty("camera", "irisWidth").toInt(); + IRIS_HEIGHT = getProperty("camera", "irisHeight").toInt(); + IRIS_FRAME_INTERVAL = getProperty("camera", "irisFrameInterval").toInt(); + IRIS_DISPLAY_WIDTH = getProperty("camera", "irisDisplayWidth").toInt(); + IRIS_DISPLAY_HEIGHT = getProperty("camera", "irisDisplayHeight").toInt(); + LOCK_FRAME_INTERVAL = getProperty("camera", "lockFrameInterval").toInt(); + + MAX_MATCH_TIME = getProperty("recognize", "maxMatchTime").toInt(); + SUCCESS_TIPS_LAST = getProperty("recognize", "successTipsLast").toInt(); + FAILURE_TIPS_LAST = getProperty("recognize", "failureTipsLast").toInt(); + MAX_FACE_TRY_COUNT = getProperty("recognize", "maxFaceTryCount").toInt(); + MAX_FACE_NOT_FOUND_COUNT = getProperty("recognize", "maxFaceNotFoundCount").toInt(); + MAX_IRIS_TRY_COUNT = getProperty("recognize", "maxIrisTryCount").toInt(); + MAX_EYE_NOT_FOUND_COUNT = getProperty("recognize", "maxEyeNotFoundCount").toInt(); + MIN_EYE_SIZE = getProperty("recognize", "minEyeSize").toInt(); + + LOG_FILE = getProperty("log", "logFile").toString(); + LOG_LEVEL = getProperty("log", "logLevel").toString(); +} + + +QVariant SettingConfig::getProperty(QString nodeName, QString keyName) { + QVariant var = this->setting->value(QString("/%1/%2").arg(nodeName).arg(keyName)); + return var; +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/dao/dao.pri b/dao/dao.pri new file mode 100644 index 0000000..300225e --- /dev/null +++ b/dao/dao.pri @@ -0,0 +1,20 @@ + +HEADERS += $$PWD/BaseDao.h +HEADERS += $$PWD/IrisDataDao.h +HEADERS += $$PWD/RecognitionRecordsDao.h +HEADERS += $$PWD/SysPersonDao.h +HEADERS += $$PWD/SysDeptDao.h +HEADERS += $$PWD/SysDictDao.h + +SOURCES += $$PWD/BaseDao.cpp +SOURCES += $$PWD/IrisDataDao.cpp +SOURCES += $$PWD/RecognitionRecordsDao.cpp +SOURCES += $$PWD/SysPersonDao.cpp +SOURCES += $$PWD/SysDeptDao.cpp +SOURCES += $$PWD/SysDictDao.cpp + +HEADERS += $$PWD/util/ConnectionManager.h +SOURCES += $$PWD/util/ConnectionManager.cpp +#HEADERS += $$PWD/util/CacheManager.h +#SOURCES += $$PWD/util/CacheManager.cpp + diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..dfbf26d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,54 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#include +#include + +Q_GLOBAL_STATIC(ConnectionManager, cm) + +ConnectionManager::ConnectionManager(QObject *parent) : QObject(parent) +{ + // 初始化数据库连接 + if (QSqlDatabase::contains("qt_sql_dafault_connection")) + { + conn = QSqlDatabase::database("qt_sql_default_connection"); + } + else + { + conn = QSqlDatabase::addDatabase("QSQLITE"); + conn.setDatabaseName(QApplication::applicationDirPath() + "/data/casic.db"); + + bool succ = conn.open(); + if (succ == true) + { +// LOG_INFO(QString("打开数据库操作正常[Open Database Success]").toStdString()); + qDebug() << "打开数据库操作正常[Open Database Success]"; + } else + { +// LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + qDebug() << "打开数据库操作失败[Open Database Failed]"; + } + } +} + +ConnectionManager::~ConnectionManager() +{ + conn.close(); +} + +ConnectionManager* ConnectionManager::getInstance() +{ + return cm; +} + +QSqlDatabase ConnectionManager::getConnection() +{ + return this->conn; +} + +qint64 ConnectionManager::generateId() +{ + return this->idWorker.nextId(); +} diff --git a/dao/util/ConnectionManager.h b/dao/util/ConnectionManager.h new file mode 100644 index 0000000..3121d9a --- /dev/null +++ b/dao/util/ConnectionManager.h @@ -0,0 +1,34 @@ +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include +#include +#include "utils/id/IdWorker.h" +//#include "utils/UtilInclude.h" + +using namespace Jiawa::Core; + +class ConnectionManager : public QObject +{ + Q_OBJECT +public: + explicit ConnectionManager(QObject *parent = nullptr); + ~ConnectionManager(); + + static ConnectionManager * getInstance(); + + QSqlDatabase getConnection(); + qint64 generateId(); + +private: + // 数据库连接 + QSqlDatabase conn; + + // 雪花id生成工具 + IdWorker &idWorker = Singleton::instance(); + +signals: + +}; + +#endif // CONNECTIONMANAGER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0a0916f --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "MainWindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..663db0f --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,43 @@ +#include "SettingConfig.h" +#include + +SettingConfig::SettingConfig() +{ + filename = QApplication::applicationDirPath() + "/conf/config.ini"; + setting = new QSettings(this->filename, QSettings::IniFormat); + + WINDOW_WIDTH = getProperty("window", "width").toInt(); + WINDOW_HEIGHT = getProperty("window", "height").toInt(); + WINDOW_BACKGROUND_COLOR = getProperty("window", "backgroundColor").toString(); + WINDOW_TITLE = getProperty("window", "title").toString(); + WINDOW_RIGHTS = getProperty("window", "copyright").toString(); + WINDOW_VERSION = getProperty("window", "version").toString(); + DEVICE_CODE = getProperty("window", "deviceCode").toString(); + + IRIS_FRAME_WIDTH = getProperty("camera", "irisFrameWidth").toInt(); + IRIS_FRAME_HEIGHT = getProperty("camera", "irisFrameHeight").toInt(); + IRIS_WIDTH = getProperty("camera", "irisWidth").toInt(); + IRIS_HEIGHT = getProperty("camera", "irisHeight").toInt(); + IRIS_FRAME_INTERVAL = getProperty("camera", "irisFrameInterval").toInt(); + IRIS_DISPLAY_WIDTH = getProperty("camera", "irisDisplayWidth").toInt(); + IRIS_DISPLAY_HEIGHT = getProperty("camera", "irisDisplayHeight").toInt(); + LOCK_FRAME_INTERVAL = getProperty("camera", "lockFrameInterval").toInt(); + + MAX_MATCH_TIME = getProperty("recognize", "maxMatchTime").toInt(); + SUCCESS_TIPS_LAST = getProperty("recognize", "successTipsLast").toInt(); + FAILURE_TIPS_LAST = getProperty("recognize", "failureTipsLast").toInt(); + MAX_FACE_TRY_COUNT = getProperty("recognize", "maxFaceTryCount").toInt(); + MAX_FACE_NOT_FOUND_COUNT = getProperty("recognize", "maxFaceNotFoundCount").toInt(); + MAX_IRIS_TRY_COUNT = getProperty("recognize", "maxIrisTryCount").toInt(); + MAX_EYE_NOT_FOUND_COUNT = getProperty("recognize", "maxEyeNotFoundCount").toInt(); + MIN_EYE_SIZE = getProperty("recognize", "minEyeSize").toInt(); + + LOG_FILE = getProperty("log", "logFile").toString(); + LOG_LEVEL = getProperty("log", "logLevel").toString(); +} + + +QVariant SettingConfig::getProperty(QString nodeName, QString keyName) { + QVariant var = this->setting->value(QString("/%1/%2").arg(nodeName).arg(keyName)); + return var; +} diff --git a/utils/SettingConfig.h b/utils/SettingConfig.h new file mode 100644 index 0000000..b65f553 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,65 @@ +#ifndef SETTINGCONFIG_H +#define SETTINGCONFIG_H + +#include +#include + +class SettingConfig : public QObject +{ +public: + ~SettingConfig() {}; + SettingConfig(const SettingConfig&)=delete; + SettingConfig& operator=(const SettingConfig&)=delete; + + static SettingConfig& getInstance() { + static SettingConfig instance; + return instance; + } + + /** + * @brief get + * @param nodeName + * @param keyName + * @return QVariant + * @title + */ + QVariant getProperty(QString nodeName, QString keyName); + + /******** 以下为需要的各类参数 ********/ + int WINDOW_WIDTH; + int WINDOW_HEIGHT; + QString WINDOW_BACKGROUND_COLOR; + QString WINDOW_TITLE; + QString WINDOW_RIGHTS; + QString WINDOW_VERSION; + QString DEVICE_CODE; + + int IRIS_FRAME_INTERVAL; + int IRIS_FRAME_WIDTH; + int IRIS_FRAME_HEIGHT; + int IRIS_WIDTH; + int IRIS_HEIGHT; + int IRIS_DISPLAY_WIDTH; + int IRIS_DISPLAY_HEIGHT; + int LOCK_FRAME_INTERVAL; + + int MAX_MATCH_TIME; + int SUCCESS_TIPS_LAST; + int FAILURE_TIPS_LAST; + int MAX_FACE_TRY_COUNT; + int MAX_FACE_NOT_FOUND_COUNT; + int MAX_IRIS_TRY_COUNT; + int MAX_EYE_NOT_FOUND_COUNT; + int MIN_EYE_SIZE; + + QString LOG_FILE; + QString LOG_LEVEL; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/dao/dao.pri b/dao/dao.pri new file mode 100644 index 0000000..300225e --- /dev/null +++ b/dao/dao.pri @@ -0,0 +1,20 @@ + +HEADERS += $$PWD/BaseDao.h +HEADERS += $$PWD/IrisDataDao.h +HEADERS += $$PWD/RecognitionRecordsDao.h +HEADERS += $$PWD/SysPersonDao.h +HEADERS += $$PWD/SysDeptDao.h +HEADERS += $$PWD/SysDictDao.h + +SOURCES += $$PWD/BaseDao.cpp +SOURCES += $$PWD/IrisDataDao.cpp +SOURCES += $$PWD/RecognitionRecordsDao.cpp +SOURCES += $$PWD/SysPersonDao.cpp +SOURCES += $$PWD/SysDeptDao.cpp +SOURCES += $$PWD/SysDictDao.cpp + +HEADERS += $$PWD/util/ConnectionManager.h +SOURCES += $$PWD/util/ConnectionManager.cpp +#HEADERS += $$PWD/util/CacheManager.h +#SOURCES += $$PWD/util/CacheManager.cpp + diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..dfbf26d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,54 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#include +#include + +Q_GLOBAL_STATIC(ConnectionManager, cm) + +ConnectionManager::ConnectionManager(QObject *parent) : QObject(parent) +{ + // 初始化数据库连接 + if (QSqlDatabase::contains("qt_sql_dafault_connection")) + { + conn = QSqlDatabase::database("qt_sql_default_connection"); + } + else + { + conn = QSqlDatabase::addDatabase("QSQLITE"); + conn.setDatabaseName(QApplication::applicationDirPath() + "/data/casic.db"); + + bool succ = conn.open(); + if (succ == true) + { +// LOG_INFO(QString("打开数据库操作正常[Open Database Success]").toStdString()); + qDebug() << "打开数据库操作正常[Open Database Success]"; + } else + { +// LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + qDebug() << "打开数据库操作失败[Open Database Failed]"; + } + } +} + +ConnectionManager::~ConnectionManager() +{ + conn.close(); +} + +ConnectionManager* ConnectionManager::getInstance() +{ + return cm; +} + +QSqlDatabase ConnectionManager::getConnection() +{ + return this->conn; +} + +qint64 ConnectionManager::generateId() +{ + return this->idWorker.nextId(); +} diff --git a/dao/util/ConnectionManager.h b/dao/util/ConnectionManager.h new file mode 100644 index 0000000..3121d9a --- /dev/null +++ b/dao/util/ConnectionManager.h @@ -0,0 +1,34 @@ +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include +#include +#include "utils/id/IdWorker.h" +//#include "utils/UtilInclude.h" + +using namespace Jiawa::Core; + +class ConnectionManager : public QObject +{ + Q_OBJECT +public: + explicit ConnectionManager(QObject *parent = nullptr); + ~ConnectionManager(); + + static ConnectionManager * getInstance(); + + QSqlDatabase getConnection(); + qint64 generateId(); + +private: + // 数据库连接 + QSqlDatabase conn; + + // 雪花id生成工具 + IdWorker &idWorker = Singleton::instance(); + +signals: + +}; + +#endif // CONNECTIONMANAGER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0a0916f --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "MainWindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..663db0f --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,43 @@ +#include "SettingConfig.h" +#include + +SettingConfig::SettingConfig() +{ + filename = QApplication::applicationDirPath() + "/conf/config.ini"; + setting = new QSettings(this->filename, QSettings::IniFormat); + + WINDOW_WIDTH = getProperty("window", "width").toInt(); + WINDOW_HEIGHT = getProperty("window", "height").toInt(); + WINDOW_BACKGROUND_COLOR = getProperty("window", "backgroundColor").toString(); + WINDOW_TITLE = getProperty("window", "title").toString(); + WINDOW_RIGHTS = getProperty("window", "copyright").toString(); + WINDOW_VERSION = getProperty("window", "version").toString(); + DEVICE_CODE = getProperty("window", "deviceCode").toString(); + + IRIS_FRAME_WIDTH = getProperty("camera", "irisFrameWidth").toInt(); + IRIS_FRAME_HEIGHT = getProperty("camera", "irisFrameHeight").toInt(); + IRIS_WIDTH = getProperty("camera", "irisWidth").toInt(); + IRIS_HEIGHT = getProperty("camera", "irisHeight").toInt(); + IRIS_FRAME_INTERVAL = getProperty("camera", "irisFrameInterval").toInt(); + IRIS_DISPLAY_WIDTH = getProperty("camera", "irisDisplayWidth").toInt(); + IRIS_DISPLAY_HEIGHT = getProperty("camera", "irisDisplayHeight").toInt(); + LOCK_FRAME_INTERVAL = getProperty("camera", "lockFrameInterval").toInt(); + + MAX_MATCH_TIME = getProperty("recognize", "maxMatchTime").toInt(); + SUCCESS_TIPS_LAST = getProperty("recognize", "successTipsLast").toInt(); + FAILURE_TIPS_LAST = getProperty("recognize", "failureTipsLast").toInt(); + MAX_FACE_TRY_COUNT = getProperty("recognize", "maxFaceTryCount").toInt(); + MAX_FACE_NOT_FOUND_COUNT = getProperty("recognize", "maxFaceNotFoundCount").toInt(); + MAX_IRIS_TRY_COUNT = getProperty("recognize", "maxIrisTryCount").toInt(); + MAX_EYE_NOT_FOUND_COUNT = getProperty("recognize", "maxEyeNotFoundCount").toInt(); + MIN_EYE_SIZE = getProperty("recognize", "minEyeSize").toInt(); + + LOG_FILE = getProperty("log", "logFile").toString(); + LOG_LEVEL = getProperty("log", "logLevel").toString(); +} + + +QVariant SettingConfig::getProperty(QString nodeName, QString keyName) { + QVariant var = this->setting->value(QString("/%1/%2").arg(nodeName).arg(keyName)); + return var; +} diff --git a/utils/SettingConfig.h b/utils/SettingConfig.h new file mode 100644 index 0000000..b65f553 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,65 @@ +#ifndef SETTINGCONFIG_H +#define SETTINGCONFIG_H + +#include +#include + +class SettingConfig : public QObject +{ +public: + ~SettingConfig() {}; + SettingConfig(const SettingConfig&)=delete; + SettingConfig& operator=(const SettingConfig&)=delete; + + static SettingConfig& getInstance() { + static SettingConfig instance; + return instance; + } + + /** + * @brief get + * @param nodeName + * @param keyName + * @return QVariant + * @title + */ + QVariant getProperty(QString nodeName, QString keyName); + + /******** 以下为需要的各类参数 ********/ + int WINDOW_WIDTH; + int WINDOW_HEIGHT; + QString WINDOW_BACKGROUND_COLOR; + QString WINDOW_TITLE; + QString WINDOW_RIGHTS; + QString WINDOW_VERSION; + QString DEVICE_CODE; + + int IRIS_FRAME_INTERVAL; + int IRIS_FRAME_WIDTH; + int IRIS_FRAME_HEIGHT; + int IRIS_WIDTH; + int IRIS_HEIGHT; + int IRIS_DISPLAY_WIDTH; + int IRIS_DISPLAY_HEIGHT; + int LOCK_FRAME_INTERVAL; + + int MAX_MATCH_TIME; + int SUCCESS_TIPS_LAST; + int FAILURE_TIPS_LAST; + int MAX_FACE_TRY_COUNT; + int MAX_FACE_NOT_FOUND_COUNT; + int MAX_IRIS_TRY_COUNT; + int MAX_EYE_NOT_FOUND_COUNT; + int MIN_EYE_SIZE; + + QString LOG_FILE; + QString LOG_LEVEL; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/id/IdWorker.h b/utils/id/IdWorker.h new file mode 100644 index 0000000..6c48aa9 --- /dev/null +++ b/utils/id/IdWorker.h @@ -0,0 +1,218 @@ +#ifndef _JW_CORE_ID_WORKER_H_ +#define _JW_CORE_ID_WORKER_H_ + +#include +#include +#include +#include +#include +#include "Noncopyable.h" +#include "Singleton.h" + +// 如果不使用 mutex, 则开启下面这个定义, 但是我发现, 还是开启 mutex 功能, 速度比较快 +// #define SNOWFLAKE_ID_WORKER_NO_LOCK + +namespace Jiawa { + + /** + * @brief 核心 + * 核心功能 + */ + namespace Core { + + /** + * @brief 分布式id生成类 + * https://segmentfault.com/a/1190000011282426 + * https://github.com/twitter/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala + * + * 64bit id: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 + * || || || | | | + * |└---------------------------时间戳--------------------------┘└中心-┘└机器-┘ └----序列号----┘ + * | + * 不用 + * SnowFlake的优点: 整体上按照时间自增排序, 并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分), 并且效率较高, 经测试, SnowFlake每秒能够产生26万ID左右. + */ + class SnowflakeIdWorker : private Noncopyable { + + // 实现单例 + friend class Singleton; + + public: + typedef unsigned int UInt; + typedef unsigned long long int UInt64; + +#ifdef SNOWFLAKE_ID_WORKER_NO_LOCK + typedef std::atomic AtomicUInt; + typedef std::atomic AtomicUInt64; +#else + typedef UInt AtomicUInt; + typedef UInt64 AtomicUInt64; +#endif + + void setWorkerId(UInt workerId) { + this->workerId = workerId; + } + + void setDatacenterId(UInt datacenterId) { + this->datacenterId = datacenterId; + } + + UInt64 getId() { + return nextId(); + } + + /** + * 获得下一个ID (该方法是线程安全的) + * + * @return SnowflakeId + */ + UInt64 nextId() { +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + std::unique_lock lock{ mutex }; + AtomicUInt64 timestamp{ 0 }; +#else + static AtomicUInt64 timestamp{ 0 }; +#endif + timestamp = timeGen(); + + // 如果当前时间小于上一次ID生成的时间戳, 说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + std::ostringstream s; + s << "clock moved backwards. Refusing to generate id for " << lastTimestamp - timestamp << " milliseconds"; + throw std::exception(std::runtime_error(s.str())); + } + + if (lastTimestamp == timestamp) { + // 如果是同一时间生成的, 则进行毫秒内序列 + sequence = (sequence + 1) & sequenceMask; + if (0 == sequence) { + // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0; + } + +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + lastTimestamp = timestamp; +#else + lastTimestamp = timestamp.load(); +#endif + + // 移位并通过或运算拼到一起组成64位的ID + return ((timestamp - twepoch) << timestampLeftShift) + | (datacenterId << datacenterIdShift) + | (workerId << workerIdShift) + | sequence; + } + + protected: + SnowflakeIdWorker() : workerId(0), datacenterId(0), sequence(0), lastTimestamp(0) { } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + UInt64 timeGen() const { + auto t = std::chrono::time_point_cast(std::chrono::high_resolution_clock::now()); + return t.time_since_epoch().count(); + } + + /** + * 阻塞到下一个毫秒, 直到获得新的时间戳 + * + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + UInt64 tilNextMillis(UInt64 lastTimestamp) const { + UInt64 timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + private: + +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + std::mutex mutex; +#endif + + /** + * 开始时间截 (2018-01-01 00:00:00.000) + */ + const UInt64 twepoch = 1514736000000; + + /** + * 机器id所占的位数 + */ + const UInt workerIdBits = 5; + + /** + * 数据中心id所占的位数 + */ + const UInt datacenterIdBits = 5; + + /** + * 序列所占的位数 + */ + const UInt sequenceBits = 12; + + /** + * 机器ID向左移12位 + */ + const UInt workerIdShift = sequenceBits; + + /** + * 数据标识id向左移17位 + */ + const UInt datacenterIdShift = workerIdShift + workerIdBits; + + /** + * 时间截向左移22位 + */ + const UInt timestampLeftShift = datacenterIdShift + datacenterIdBits; + + /** + * 支持的最大机器id, 结果是31 + */ + const UInt maxWorkerId = -1 ^ (-1 << workerIdBits); + + /** + * 支持的最大数据中心id, 结果是31 + */ + const UInt maxDatacenterId = -1 ^ (-1 << datacenterIdBits); + + /** + * 生成序列的掩码, 这里为4095 + */ + const UInt sequenceMask = -1 ^ (-1 << sequenceBits); + + /** + * 工作机器id(0~31) + */ + UInt workerId; + + /** + * 数据中心id(0~31) + */ + UInt datacenterId; + + /** + * 毫秒内序列(0~4095) + */ + AtomicUInt sequence{ 0 }; + + /** + * 上次生成ID的时间截 + */ + AtomicUInt64 lastTimestamp{ 0 }; + + }; + + typedef SnowflakeIdWorker IdWorker; + } +} + +#endif // _JW_CORE_ID_WORKER_H_ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/dao/dao.pri b/dao/dao.pri new file mode 100644 index 0000000..300225e --- /dev/null +++ b/dao/dao.pri @@ -0,0 +1,20 @@ + +HEADERS += $$PWD/BaseDao.h +HEADERS += $$PWD/IrisDataDao.h +HEADERS += $$PWD/RecognitionRecordsDao.h +HEADERS += $$PWD/SysPersonDao.h +HEADERS += $$PWD/SysDeptDao.h +HEADERS += $$PWD/SysDictDao.h + +SOURCES += $$PWD/BaseDao.cpp +SOURCES += $$PWD/IrisDataDao.cpp +SOURCES += $$PWD/RecognitionRecordsDao.cpp +SOURCES += $$PWD/SysPersonDao.cpp +SOURCES += $$PWD/SysDeptDao.cpp +SOURCES += $$PWD/SysDictDao.cpp + +HEADERS += $$PWD/util/ConnectionManager.h +SOURCES += $$PWD/util/ConnectionManager.cpp +#HEADERS += $$PWD/util/CacheManager.h +#SOURCES += $$PWD/util/CacheManager.cpp + diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..dfbf26d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,54 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#include +#include + +Q_GLOBAL_STATIC(ConnectionManager, cm) + +ConnectionManager::ConnectionManager(QObject *parent) : QObject(parent) +{ + // 初始化数据库连接 + if (QSqlDatabase::contains("qt_sql_dafault_connection")) + { + conn = QSqlDatabase::database("qt_sql_default_connection"); + } + else + { + conn = QSqlDatabase::addDatabase("QSQLITE"); + conn.setDatabaseName(QApplication::applicationDirPath() + "/data/casic.db"); + + bool succ = conn.open(); + if (succ == true) + { +// LOG_INFO(QString("打开数据库操作正常[Open Database Success]").toStdString()); + qDebug() << "打开数据库操作正常[Open Database Success]"; + } else + { +// LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + qDebug() << "打开数据库操作失败[Open Database Failed]"; + } + } +} + +ConnectionManager::~ConnectionManager() +{ + conn.close(); +} + +ConnectionManager* ConnectionManager::getInstance() +{ + return cm; +} + +QSqlDatabase ConnectionManager::getConnection() +{ + return this->conn; +} + +qint64 ConnectionManager::generateId() +{ + return this->idWorker.nextId(); +} diff --git a/dao/util/ConnectionManager.h b/dao/util/ConnectionManager.h new file mode 100644 index 0000000..3121d9a --- /dev/null +++ b/dao/util/ConnectionManager.h @@ -0,0 +1,34 @@ +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include +#include +#include "utils/id/IdWorker.h" +//#include "utils/UtilInclude.h" + +using namespace Jiawa::Core; + +class ConnectionManager : public QObject +{ + Q_OBJECT +public: + explicit ConnectionManager(QObject *parent = nullptr); + ~ConnectionManager(); + + static ConnectionManager * getInstance(); + + QSqlDatabase getConnection(); + qint64 generateId(); + +private: + // 数据库连接 + QSqlDatabase conn; + + // 雪花id生成工具 + IdWorker &idWorker = Singleton::instance(); + +signals: + +}; + +#endif // CONNECTIONMANAGER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0a0916f --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "MainWindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..663db0f --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,43 @@ +#include "SettingConfig.h" +#include + +SettingConfig::SettingConfig() +{ + filename = QApplication::applicationDirPath() + "/conf/config.ini"; + setting = new QSettings(this->filename, QSettings::IniFormat); + + WINDOW_WIDTH = getProperty("window", "width").toInt(); + WINDOW_HEIGHT = getProperty("window", "height").toInt(); + WINDOW_BACKGROUND_COLOR = getProperty("window", "backgroundColor").toString(); + WINDOW_TITLE = getProperty("window", "title").toString(); + WINDOW_RIGHTS = getProperty("window", "copyright").toString(); + WINDOW_VERSION = getProperty("window", "version").toString(); + DEVICE_CODE = getProperty("window", "deviceCode").toString(); + + IRIS_FRAME_WIDTH = getProperty("camera", "irisFrameWidth").toInt(); + IRIS_FRAME_HEIGHT = getProperty("camera", "irisFrameHeight").toInt(); + IRIS_WIDTH = getProperty("camera", "irisWidth").toInt(); + IRIS_HEIGHT = getProperty("camera", "irisHeight").toInt(); + IRIS_FRAME_INTERVAL = getProperty("camera", "irisFrameInterval").toInt(); + IRIS_DISPLAY_WIDTH = getProperty("camera", "irisDisplayWidth").toInt(); + IRIS_DISPLAY_HEIGHT = getProperty("camera", "irisDisplayHeight").toInt(); + LOCK_FRAME_INTERVAL = getProperty("camera", "lockFrameInterval").toInt(); + + MAX_MATCH_TIME = getProperty("recognize", "maxMatchTime").toInt(); + SUCCESS_TIPS_LAST = getProperty("recognize", "successTipsLast").toInt(); + FAILURE_TIPS_LAST = getProperty("recognize", "failureTipsLast").toInt(); + MAX_FACE_TRY_COUNT = getProperty("recognize", "maxFaceTryCount").toInt(); + MAX_FACE_NOT_FOUND_COUNT = getProperty("recognize", "maxFaceNotFoundCount").toInt(); + MAX_IRIS_TRY_COUNT = getProperty("recognize", "maxIrisTryCount").toInt(); + MAX_EYE_NOT_FOUND_COUNT = getProperty("recognize", "maxEyeNotFoundCount").toInt(); + MIN_EYE_SIZE = getProperty("recognize", "minEyeSize").toInt(); + + LOG_FILE = getProperty("log", "logFile").toString(); + LOG_LEVEL = getProperty("log", "logLevel").toString(); +} + + +QVariant SettingConfig::getProperty(QString nodeName, QString keyName) { + QVariant var = this->setting->value(QString("/%1/%2").arg(nodeName).arg(keyName)); + return var; +} diff --git a/utils/SettingConfig.h b/utils/SettingConfig.h new file mode 100644 index 0000000..b65f553 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,65 @@ +#ifndef SETTINGCONFIG_H +#define SETTINGCONFIG_H + +#include +#include + +class SettingConfig : public QObject +{ +public: + ~SettingConfig() {}; + SettingConfig(const SettingConfig&)=delete; + SettingConfig& operator=(const SettingConfig&)=delete; + + static SettingConfig& getInstance() { + static SettingConfig instance; + return instance; + } + + /** + * @brief get + * @param nodeName + * @param keyName + * @return QVariant + * @title + */ + QVariant getProperty(QString nodeName, QString keyName); + + /******** 以下为需要的各类参数 ********/ + int WINDOW_WIDTH; + int WINDOW_HEIGHT; + QString WINDOW_BACKGROUND_COLOR; + QString WINDOW_TITLE; + QString WINDOW_RIGHTS; + QString WINDOW_VERSION; + QString DEVICE_CODE; + + int IRIS_FRAME_INTERVAL; + int IRIS_FRAME_WIDTH; + int IRIS_FRAME_HEIGHT; + int IRIS_WIDTH; + int IRIS_HEIGHT; + int IRIS_DISPLAY_WIDTH; + int IRIS_DISPLAY_HEIGHT; + int LOCK_FRAME_INTERVAL; + + int MAX_MATCH_TIME; + int SUCCESS_TIPS_LAST; + int FAILURE_TIPS_LAST; + int MAX_FACE_TRY_COUNT; + int MAX_FACE_NOT_FOUND_COUNT; + int MAX_IRIS_TRY_COUNT; + int MAX_EYE_NOT_FOUND_COUNT; + int MIN_EYE_SIZE; + + QString LOG_FILE; + QString LOG_LEVEL; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/id/IdWorker.h b/utils/id/IdWorker.h new file mode 100644 index 0000000..6c48aa9 --- /dev/null +++ b/utils/id/IdWorker.h @@ -0,0 +1,218 @@ +#ifndef _JW_CORE_ID_WORKER_H_ +#define _JW_CORE_ID_WORKER_H_ + +#include +#include +#include +#include +#include +#include "Noncopyable.h" +#include "Singleton.h" + +// 如果不使用 mutex, 则开启下面这个定义, 但是我发现, 还是开启 mutex 功能, 速度比较快 +// #define SNOWFLAKE_ID_WORKER_NO_LOCK + +namespace Jiawa { + + /** + * @brief 核心 + * 核心功能 + */ + namespace Core { + + /** + * @brief 分布式id生成类 + * https://segmentfault.com/a/1190000011282426 + * https://github.com/twitter/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala + * + * 64bit id: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 + * || || || | | | + * |└---------------------------时间戳--------------------------┘└中心-┘└机器-┘ └----序列号----┘ + * | + * 不用 + * SnowFlake的优点: 整体上按照时间自增排序, 并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分), 并且效率较高, 经测试, SnowFlake每秒能够产生26万ID左右. + */ + class SnowflakeIdWorker : private Noncopyable { + + // 实现单例 + friend class Singleton; + + public: + typedef unsigned int UInt; + typedef unsigned long long int UInt64; + +#ifdef SNOWFLAKE_ID_WORKER_NO_LOCK + typedef std::atomic AtomicUInt; + typedef std::atomic AtomicUInt64; +#else + typedef UInt AtomicUInt; + typedef UInt64 AtomicUInt64; +#endif + + void setWorkerId(UInt workerId) { + this->workerId = workerId; + } + + void setDatacenterId(UInt datacenterId) { + this->datacenterId = datacenterId; + } + + UInt64 getId() { + return nextId(); + } + + /** + * 获得下一个ID (该方法是线程安全的) + * + * @return SnowflakeId + */ + UInt64 nextId() { +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + std::unique_lock lock{ mutex }; + AtomicUInt64 timestamp{ 0 }; +#else + static AtomicUInt64 timestamp{ 0 }; +#endif + timestamp = timeGen(); + + // 如果当前时间小于上一次ID生成的时间戳, 说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + std::ostringstream s; + s << "clock moved backwards. Refusing to generate id for " << lastTimestamp - timestamp << " milliseconds"; + throw std::exception(std::runtime_error(s.str())); + } + + if (lastTimestamp == timestamp) { + // 如果是同一时间生成的, 则进行毫秒内序列 + sequence = (sequence + 1) & sequenceMask; + if (0 == sequence) { + // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0; + } + +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + lastTimestamp = timestamp; +#else + lastTimestamp = timestamp.load(); +#endif + + // 移位并通过或运算拼到一起组成64位的ID + return ((timestamp - twepoch) << timestampLeftShift) + | (datacenterId << datacenterIdShift) + | (workerId << workerIdShift) + | sequence; + } + + protected: + SnowflakeIdWorker() : workerId(0), datacenterId(0), sequence(0), lastTimestamp(0) { } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + UInt64 timeGen() const { + auto t = std::chrono::time_point_cast(std::chrono::high_resolution_clock::now()); + return t.time_since_epoch().count(); + } + + /** + * 阻塞到下一个毫秒, 直到获得新的时间戳 + * + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + UInt64 tilNextMillis(UInt64 lastTimestamp) const { + UInt64 timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + private: + +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + std::mutex mutex; +#endif + + /** + * 开始时间截 (2018-01-01 00:00:00.000) + */ + const UInt64 twepoch = 1514736000000; + + /** + * 机器id所占的位数 + */ + const UInt workerIdBits = 5; + + /** + * 数据中心id所占的位数 + */ + const UInt datacenterIdBits = 5; + + /** + * 序列所占的位数 + */ + const UInt sequenceBits = 12; + + /** + * 机器ID向左移12位 + */ + const UInt workerIdShift = sequenceBits; + + /** + * 数据标识id向左移17位 + */ + const UInt datacenterIdShift = workerIdShift + workerIdBits; + + /** + * 时间截向左移22位 + */ + const UInt timestampLeftShift = datacenterIdShift + datacenterIdBits; + + /** + * 支持的最大机器id, 结果是31 + */ + const UInt maxWorkerId = -1 ^ (-1 << workerIdBits); + + /** + * 支持的最大数据中心id, 结果是31 + */ + const UInt maxDatacenterId = -1 ^ (-1 << datacenterIdBits); + + /** + * 生成序列的掩码, 这里为4095 + */ + const UInt sequenceMask = -1 ^ (-1 << sequenceBits); + + /** + * 工作机器id(0~31) + */ + UInt workerId; + + /** + * 数据中心id(0~31) + */ + UInt datacenterId; + + /** + * 毫秒内序列(0~4095) + */ + AtomicUInt sequence{ 0 }; + + /** + * 上次生成ID的时间截 + */ + AtomicUInt64 lastTimestamp{ 0 }; + + }; + + typedef SnowflakeIdWorker IdWorker; + } +} + +#endif // _JW_CORE_ID_WORKER_H_ diff --git a/utils/id/Noncopyable.h b/utils/id/Noncopyable.h new file mode 100644 index 0000000..d87f58a --- /dev/null +++ b/utils/id/Noncopyable.h @@ -0,0 +1,31 @@ +#ifndef _JW_CORE_NONCOPYABLE_H_ +#define _JW_CORE_NONCOPYABLE_H_ + + +// Private copy constructor and copy assignment ensure classes derived from +// class noncopyable cannot be copied. + +namespace Jiawa { + namespace Core { + + // protection from unintended ADL(Argument Dependent Lookup) + namespace Noncopyable_ { + + class Noncopyable + { + protected: + Noncopyable() = default; + ~Noncopyable() = default; + + Noncopyable(const Noncopyable&) = delete; + Noncopyable(const Noncopyable&&) = delete; + Noncopyable& operator=(const Noncopyable&) = delete; + Noncopyable& operator=(const Noncopyable&&) = delete; + }; + } + + typedef Noncopyable_::Noncopyable Noncopyable; + } +} + +#endif // _JW_CORE_NONCOPYABLE_H_ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/dao/dao.pri b/dao/dao.pri new file mode 100644 index 0000000..300225e --- /dev/null +++ b/dao/dao.pri @@ -0,0 +1,20 @@ + +HEADERS += $$PWD/BaseDao.h +HEADERS += $$PWD/IrisDataDao.h +HEADERS += $$PWD/RecognitionRecordsDao.h +HEADERS += $$PWD/SysPersonDao.h +HEADERS += $$PWD/SysDeptDao.h +HEADERS += $$PWD/SysDictDao.h + +SOURCES += $$PWD/BaseDao.cpp +SOURCES += $$PWD/IrisDataDao.cpp +SOURCES += $$PWD/RecognitionRecordsDao.cpp +SOURCES += $$PWD/SysPersonDao.cpp +SOURCES += $$PWD/SysDeptDao.cpp +SOURCES += $$PWD/SysDictDao.cpp + +HEADERS += $$PWD/util/ConnectionManager.h +SOURCES += $$PWD/util/ConnectionManager.cpp +#HEADERS += $$PWD/util/CacheManager.h +#SOURCES += $$PWD/util/CacheManager.cpp + diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..dfbf26d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,54 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#include +#include + +Q_GLOBAL_STATIC(ConnectionManager, cm) + +ConnectionManager::ConnectionManager(QObject *parent) : QObject(parent) +{ + // 初始化数据库连接 + if (QSqlDatabase::contains("qt_sql_dafault_connection")) + { + conn = QSqlDatabase::database("qt_sql_default_connection"); + } + else + { + conn = QSqlDatabase::addDatabase("QSQLITE"); + conn.setDatabaseName(QApplication::applicationDirPath() + "/data/casic.db"); + + bool succ = conn.open(); + if (succ == true) + { +// LOG_INFO(QString("打开数据库操作正常[Open Database Success]").toStdString()); + qDebug() << "打开数据库操作正常[Open Database Success]"; + } else + { +// LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + qDebug() << "打开数据库操作失败[Open Database Failed]"; + } + } +} + +ConnectionManager::~ConnectionManager() +{ + conn.close(); +} + +ConnectionManager* ConnectionManager::getInstance() +{ + return cm; +} + +QSqlDatabase ConnectionManager::getConnection() +{ + return this->conn; +} + +qint64 ConnectionManager::generateId() +{ + return this->idWorker.nextId(); +} diff --git a/dao/util/ConnectionManager.h b/dao/util/ConnectionManager.h new file mode 100644 index 0000000..3121d9a --- /dev/null +++ b/dao/util/ConnectionManager.h @@ -0,0 +1,34 @@ +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include +#include +#include "utils/id/IdWorker.h" +//#include "utils/UtilInclude.h" + +using namespace Jiawa::Core; + +class ConnectionManager : public QObject +{ + Q_OBJECT +public: + explicit ConnectionManager(QObject *parent = nullptr); + ~ConnectionManager(); + + static ConnectionManager * getInstance(); + + QSqlDatabase getConnection(); + qint64 generateId(); + +private: + // 数据库连接 + QSqlDatabase conn; + + // 雪花id生成工具 + IdWorker &idWorker = Singleton::instance(); + +signals: + +}; + +#endif // CONNECTIONMANAGER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0a0916f --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "MainWindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..663db0f --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,43 @@ +#include "SettingConfig.h" +#include + +SettingConfig::SettingConfig() +{ + filename = QApplication::applicationDirPath() + "/conf/config.ini"; + setting = new QSettings(this->filename, QSettings::IniFormat); + + WINDOW_WIDTH = getProperty("window", "width").toInt(); + WINDOW_HEIGHT = getProperty("window", "height").toInt(); + WINDOW_BACKGROUND_COLOR = getProperty("window", "backgroundColor").toString(); + WINDOW_TITLE = getProperty("window", "title").toString(); + WINDOW_RIGHTS = getProperty("window", "copyright").toString(); + WINDOW_VERSION = getProperty("window", "version").toString(); + DEVICE_CODE = getProperty("window", "deviceCode").toString(); + + IRIS_FRAME_WIDTH = getProperty("camera", "irisFrameWidth").toInt(); + IRIS_FRAME_HEIGHT = getProperty("camera", "irisFrameHeight").toInt(); + IRIS_WIDTH = getProperty("camera", "irisWidth").toInt(); + IRIS_HEIGHT = getProperty("camera", "irisHeight").toInt(); + IRIS_FRAME_INTERVAL = getProperty("camera", "irisFrameInterval").toInt(); + IRIS_DISPLAY_WIDTH = getProperty("camera", "irisDisplayWidth").toInt(); + IRIS_DISPLAY_HEIGHT = getProperty("camera", "irisDisplayHeight").toInt(); + LOCK_FRAME_INTERVAL = getProperty("camera", "lockFrameInterval").toInt(); + + MAX_MATCH_TIME = getProperty("recognize", "maxMatchTime").toInt(); + SUCCESS_TIPS_LAST = getProperty("recognize", "successTipsLast").toInt(); + FAILURE_TIPS_LAST = getProperty("recognize", "failureTipsLast").toInt(); + MAX_FACE_TRY_COUNT = getProperty("recognize", "maxFaceTryCount").toInt(); + MAX_FACE_NOT_FOUND_COUNT = getProperty("recognize", "maxFaceNotFoundCount").toInt(); + MAX_IRIS_TRY_COUNT = getProperty("recognize", "maxIrisTryCount").toInt(); + MAX_EYE_NOT_FOUND_COUNT = getProperty("recognize", "maxEyeNotFoundCount").toInt(); + MIN_EYE_SIZE = getProperty("recognize", "minEyeSize").toInt(); + + LOG_FILE = getProperty("log", "logFile").toString(); + LOG_LEVEL = getProperty("log", "logLevel").toString(); +} + + +QVariant SettingConfig::getProperty(QString nodeName, QString keyName) { + QVariant var = this->setting->value(QString("/%1/%2").arg(nodeName).arg(keyName)); + return var; +} diff --git a/utils/SettingConfig.h b/utils/SettingConfig.h new file mode 100644 index 0000000..b65f553 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,65 @@ +#ifndef SETTINGCONFIG_H +#define SETTINGCONFIG_H + +#include +#include + +class SettingConfig : public QObject +{ +public: + ~SettingConfig() {}; + SettingConfig(const SettingConfig&)=delete; + SettingConfig& operator=(const SettingConfig&)=delete; + + static SettingConfig& getInstance() { + static SettingConfig instance; + return instance; + } + + /** + * @brief get + * @param nodeName + * @param keyName + * @return QVariant + * @title + */ + QVariant getProperty(QString nodeName, QString keyName); + + /******** 以下为需要的各类参数 ********/ + int WINDOW_WIDTH; + int WINDOW_HEIGHT; + QString WINDOW_BACKGROUND_COLOR; + QString WINDOW_TITLE; + QString WINDOW_RIGHTS; + QString WINDOW_VERSION; + QString DEVICE_CODE; + + int IRIS_FRAME_INTERVAL; + int IRIS_FRAME_WIDTH; + int IRIS_FRAME_HEIGHT; + int IRIS_WIDTH; + int IRIS_HEIGHT; + int IRIS_DISPLAY_WIDTH; + int IRIS_DISPLAY_HEIGHT; + int LOCK_FRAME_INTERVAL; + + int MAX_MATCH_TIME; + int SUCCESS_TIPS_LAST; + int FAILURE_TIPS_LAST; + int MAX_FACE_TRY_COUNT; + int MAX_FACE_NOT_FOUND_COUNT; + int MAX_IRIS_TRY_COUNT; + int MAX_EYE_NOT_FOUND_COUNT; + int MIN_EYE_SIZE; + + QString LOG_FILE; + QString LOG_LEVEL; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/id/IdWorker.h b/utils/id/IdWorker.h new file mode 100644 index 0000000..6c48aa9 --- /dev/null +++ b/utils/id/IdWorker.h @@ -0,0 +1,218 @@ +#ifndef _JW_CORE_ID_WORKER_H_ +#define _JW_CORE_ID_WORKER_H_ + +#include +#include +#include +#include +#include +#include "Noncopyable.h" +#include "Singleton.h" + +// 如果不使用 mutex, 则开启下面这个定义, 但是我发现, 还是开启 mutex 功能, 速度比较快 +// #define SNOWFLAKE_ID_WORKER_NO_LOCK + +namespace Jiawa { + + /** + * @brief 核心 + * 核心功能 + */ + namespace Core { + + /** + * @brief 分布式id生成类 + * https://segmentfault.com/a/1190000011282426 + * https://github.com/twitter/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala + * + * 64bit id: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 + * || || || | | | + * |└---------------------------时间戳--------------------------┘└中心-┘└机器-┘ └----序列号----┘ + * | + * 不用 + * SnowFlake的优点: 整体上按照时间自增排序, 并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分), 并且效率较高, 经测试, SnowFlake每秒能够产生26万ID左右. + */ + class SnowflakeIdWorker : private Noncopyable { + + // 实现单例 + friend class Singleton; + + public: + typedef unsigned int UInt; + typedef unsigned long long int UInt64; + +#ifdef SNOWFLAKE_ID_WORKER_NO_LOCK + typedef std::atomic AtomicUInt; + typedef std::atomic AtomicUInt64; +#else + typedef UInt AtomicUInt; + typedef UInt64 AtomicUInt64; +#endif + + void setWorkerId(UInt workerId) { + this->workerId = workerId; + } + + void setDatacenterId(UInt datacenterId) { + this->datacenterId = datacenterId; + } + + UInt64 getId() { + return nextId(); + } + + /** + * 获得下一个ID (该方法是线程安全的) + * + * @return SnowflakeId + */ + UInt64 nextId() { +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + std::unique_lock lock{ mutex }; + AtomicUInt64 timestamp{ 0 }; +#else + static AtomicUInt64 timestamp{ 0 }; +#endif + timestamp = timeGen(); + + // 如果当前时间小于上一次ID生成的时间戳, 说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + std::ostringstream s; + s << "clock moved backwards. Refusing to generate id for " << lastTimestamp - timestamp << " milliseconds"; + throw std::exception(std::runtime_error(s.str())); + } + + if (lastTimestamp == timestamp) { + // 如果是同一时间生成的, 则进行毫秒内序列 + sequence = (sequence + 1) & sequenceMask; + if (0 == sequence) { + // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0; + } + +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + lastTimestamp = timestamp; +#else + lastTimestamp = timestamp.load(); +#endif + + // 移位并通过或运算拼到一起组成64位的ID + return ((timestamp - twepoch) << timestampLeftShift) + | (datacenterId << datacenterIdShift) + | (workerId << workerIdShift) + | sequence; + } + + protected: + SnowflakeIdWorker() : workerId(0), datacenterId(0), sequence(0), lastTimestamp(0) { } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + UInt64 timeGen() const { + auto t = std::chrono::time_point_cast(std::chrono::high_resolution_clock::now()); + return t.time_since_epoch().count(); + } + + /** + * 阻塞到下一个毫秒, 直到获得新的时间戳 + * + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + UInt64 tilNextMillis(UInt64 lastTimestamp) const { + UInt64 timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + private: + +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + std::mutex mutex; +#endif + + /** + * 开始时间截 (2018-01-01 00:00:00.000) + */ + const UInt64 twepoch = 1514736000000; + + /** + * 机器id所占的位数 + */ + const UInt workerIdBits = 5; + + /** + * 数据中心id所占的位数 + */ + const UInt datacenterIdBits = 5; + + /** + * 序列所占的位数 + */ + const UInt sequenceBits = 12; + + /** + * 机器ID向左移12位 + */ + const UInt workerIdShift = sequenceBits; + + /** + * 数据标识id向左移17位 + */ + const UInt datacenterIdShift = workerIdShift + workerIdBits; + + /** + * 时间截向左移22位 + */ + const UInt timestampLeftShift = datacenterIdShift + datacenterIdBits; + + /** + * 支持的最大机器id, 结果是31 + */ + const UInt maxWorkerId = -1 ^ (-1 << workerIdBits); + + /** + * 支持的最大数据中心id, 结果是31 + */ + const UInt maxDatacenterId = -1 ^ (-1 << datacenterIdBits); + + /** + * 生成序列的掩码, 这里为4095 + */ + const UInt sequenceMask = -1 ^ (-1 << sequenceBits); + + /** + * 工作机器id(0~31) + */ + UInt workerId; + + /** + * 数据中心id(0~31) + */ + UInt datacenterId; + + /** + * 毫秒内序列(0~4095) + */ + AtomicUInt sequence{ 0 }; + + /** + * 上次生成ID的时间截 + */ + AtomicUInt64 lastTimestamp{ 0 }; + + }; + + typedef SnowflakeIdWorker IdWorker; + } +} + +#endif // _JW_CORE_ID_WORKER_H_ diff --git a/utils/id/Noncopyable.h b/utils/id/Noncopyable.h new file mode 100644 index 0000000..d87f58a --- /dev/null +++ b/utils/id/Noncopyable.h @@ -0,0 +1,31 @@ +#ifndef _JW_CORE_NONCOPYABLE_H_ +#define _JW_CORE_NONCOPYABLE_H_ + + +// Private copy constructor and copy assignment ensure classes derived from +// class noncopyable cannot be copied. + +namespace Jiawa { + namespace Core { + + // protection from unintended ADL(Argument Dependent Lookup) + namespace Noncopyable_ { + + class Noncopyable + { + protected: + Noncopyable() = default; + ~Noncopyable() = default; + + Noncopyable(const Noncopyable&) = delete; + Noncopyable(const Noncopyable&&) = delete; + Noncopyable& operator=(const Noncopyable&) = delete; + Noncopyable& operator=(const Noncopyable&&) = delete; + }; + } + + typedef Noncopyable_::Noncopyable Noncopyable; + } +} + +#endif // _JW_CORE_NONCOPYABLE_H_ diff --git a/utils/id/Singleton.h b/utils/id/Singleton.h new file mode 100644 index 0000000..d1c0553 --- /dev/null +++ b/utils/id/Singleton.h @@ -0,0 +1,60 @@ +#ifndef _JW_CORE_SINGLETON_H_ +#define _JW_CORE_SINGLETON_H_ + +namespace Jiawa { + namespace Core { + + // boost/container/detail/singleton.hpp + // http://blog.csdn.net/fullsail/article/details/8483106 + // T must be: no-throw default constructible and no-throw destructible + template + class Singleton { + private: + Singleton() = default; + ~Singleton() = default; + + private: + struct object_creator + { + // This constructor does nothing more than ensure that instance() + // is called before main() begins, thus creating the static + // T object before multithreading race issues can come up. + object_creator() { Singleton::instance(); } + inline void do_nothing() const { } + }; + static object_creator create_object; + + private: + Singleton(const Singleton&) = delete; + Singleton(const Singleton&&) = delete; + Singleton& operator=(const Singleton&) = delete; + Singleton& operator=(const Singleton&&) = delete; + + public: + typedef T object_type; + + // If, at any point (in user code), Singleton::instance() + // is called, then the following function is instantiated. + static object_type& instance() + { + // This is the object that we return a reference to. + // It is guaranteed to be created before main() begins because of + // the next line. + static object_type obj; + + // The following line does nothing else than force the instantiation + // of Singleton::create_object, whose constructor is + // called before main() begins. + create_object.do_nothing(); + + return obj; + } + }; + + template + typename Singleton::object_creator Singleton::create_object; + } +} + +#endif // _JW_CORE_SINGLETON_H_ + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/dao/dao.pri b/dao/dao.pri new file mode 100644 index 0000000..300225e --- /dev/null +++ b/dao/dao.pri @@ -0,0 +1,20 @@ + +HEADERS += $$PWD/BaseDao.h +HEADERS += $$PWD/IrisDataDao.h +HEADERS += $$PWD/RecognitionRecordsDao.h +HEADERS += $$PWD/SysPersonDao.h +HEADERS += $$PWD/SysDeptDao.h +HEADERS += $$PWD/SysDictDao.h + +SOURCES += $$PWD/BaseDao.cpp +SOURCES += $$PWD/IrisDataDao.cpp +SOURCES += $$PWD/RecognitionRecordsDao.cpp +SOURCES += $$PWD/SysPersonDao.cpp +SOURCES += $$PWD/SysDeptDao.cpp +SOURCES += $$PWD/SysDictDao.cpp + +HEADERS += $$PWD/util/ConnectionManager.h +SOURCES += $$PWD/util/ConnectionManager.cpp +#HEADERS += $$PWD/util/CacheManager.h +#SOURCES += $$PWD/util/CacheManager.cpp + diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..dfbf26d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,54 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#include +#include + +Q_GLOBAL_STATIC(ConnectionManager, cm) + +ConnectionManager::ConnectionManager(QObject *parent) : QObject(parent) +{ + // 初始化数据库连接 + if (QSqlDatabase::contains("qt_sql_dafault_connection")) + { + conn = QSqlDatabase::database("qt_sql_default_connection"); + } + else + { + conn = QSqlDatabase::addDatabase("QSQLITE"); + conn.setDatabaseName(QApplication::applicationDirPath() + "/data/casic.db"); + + bool succ = conn.open(); + if (succ == true) + { +// LOG_INFO(QString("打开数据库操作正常[Open Database Success]").toStdString()); + qDebug() << "打开数据库操作正常[Open Database Success]"; + } else + { +// LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + qDebug() << "打开数据库操作失败[Open Database Failed]"; + } + } +} + +ConnectionManager::~ConnectionManager() +{ + conn.close(); +} + +ConnectionManager* ConnectionManager::getInstance() +{ + return cm; +} + +QSqlDatabase ConnectionManager::getConnection() +{ + return this->conn; +} + +qint64 ConnectionManager::generateId() +{ + return this->idWorker.nextId(); +} diff --git a/dao/util/ConnectionManager.h b/dao/util/ConnectionManager.h new file mode 100644 index 0000000..3121d9a --- /dev/null +++ b/dao/util/ConnectionManager.h @@ -0,0 +1,34 @@ +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include +#include +#include "utils/id/IdWorker.h" +//#include "utils/UtilInclude.h" + +using namespace Jiawa::Core; + +class ConnectionManager : public QObject +{ + Q_OBJECT +public: + explicit ConnectionManager(QObject *parent = nullptr); + ~ConnectionManager(); + + static ConnectionManager * getInstance(); + + QSqlDatabase getConnection(); + qint64 generateId(); + +private: + // 数据库连接 + QSqlDatabase conn; + + // 雪花id生成工具 + IdWorker &idWorker = Singleton::instance(); + +signals: + +}; + +#endif // CONNECTIONMANAGER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0a0916f --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "MainWindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..663db0f --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,43 @@ +#include "SettingConfig.h" +#include + +SettingConfig::SettingConfig() +{ + filename = QApplication::applicationDirPath() + "/conf/config.ini"; + setting = new QSettings(this->filename, QSettings::IniFormat); + + WINDOW_WIDTH = getProperty("window", "width").toInt(); + WINDOW_HEIGHT = getProperty("window", "height").toInt(); + WINDOW_BACKGROUND_COLOR = getProperty("window", "backgroundColor").toString(); + WINDOW_TITLE = getProperty("window", "title").toString(); + WINDOW_RIGHTS = getProperty("window", "copyright").toString(); + WINDOW_VERSION = getProperty("window", "version").toString(); + DEVICE_CODE = getProperty("window", "deviceCode").toString(); + + IRIS_FRAME_WIDTH = getProperty("camera", "irisFrameWidth").toInt(); + IRIS_FRAME_HEIGHT = getProperty("camera", "irisFrameHeight").toInt(); + IRIS_WIDTH = getProperty("camera", "irisWidth").toInt(); + IRIS_HEIGHT = getProperty("camera", "irisHeight").toInt(); + IRIS_FRAME_INTERVAL = getProperty("camera", "irisFrameInterval").toInt(); + IRIS_DISPLAY_WIDTH = getProperty("camera", "irisDisplayWidth").toInt(); + IRIS_DISPLAY_HEIGHT = getProperty("camera", "irisDisplayHeight").toInt(); + LOCK_FRAME_INTERVAL = getProperty("camera", "lockFrameInterval").toInt(); + + MAX_MATCH_TIME = getProperty("recognize", "maxMatchTime").toInt(); + SUCCESS_TIPS_LAST = getProperty("recognize", "successTipsLast").toInt(); + FAILURE_TIPS_LAST = getProperty("recognize", "failureTipsLast").toInt(); + MAX_FACE_TRY_COUNT = getProperty("recognize", "maxFaceTryCount").toInt(); + MAX_FACE_NOT_FOUND_COUNT = getProperty("recognize", "maxFaceNotFoundCount").toInt(); + MAX_IRIS_TRY_COUNT = getProperty("recognize", "maxIrisTryCount").toInt(); + MAX_EYE_NOT_FOUND_COUNT = getProperty("recognize", "maxEyeNotFoundCount").toInt(); + MIN_EYE_SIZE = getProperty("recognize", "minEyeSize").toInt(); + + LOG_FILE = getProperty("log", "logFile").toString(); + LOG_LEVEL = getProperty("log", "logLevel").toString(); +} + + +QVariant SettingConfig::getProperty(QString nodeName, QString keyName) { + QVariant var = this->setting->value(QString("/%1/%2").arg(nodeName).arg(keyName)); + return var; +} diff --git a/utils/SettingConfig.h b/utils/SettingConfig.h new file mode 100644 index 0000000..b65f553 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,65 @@ +#ifndef SETTINGCONFIG_H +#define SETTINGCONFIG_H + +#include +#include + +class SettingConfig : public QObject +{ +public: + ~SettingConfig() {}; + SettingConfig(const SettingConfig&)=delete; + SettingConfig& operator=(const SettingConfig&)=delete; + + static SettingConfig& getInstance() { + static SettingConfig instance; + return instance; + } + + /** + * @brief get + * @param nodeName + * @param keyName + * @return QVariant + * @title + */ + QVariant getProperty(QString nodeName, QString keyName); + + /******** 以下为需要的各类参数 ********/ + int WINDOW_WIDTH; + int WINDOW_HEIGHT; + QString WINDOW_BACKGROUND_COLOR; + QString WINDOW_TITLE; + QString WINDOW_RIGHTS; + QString WINDOW_VERSION; + QString DEVICE_CODE; + + int IRIS_FRAME_INTERVAL; + int IRIS_FRAME_WIDTH; + int IRIS_FRAME_HEIGHT; + int IRIS_WIDTH; + int IRIS_HEIGHT; + int IRIS_DISPLAY_WIDTH; + int IRIS_DISPLAY_HEIGHT; + int LOCK_FRAME_INTERVAL; + + int MAX_MATCH_TIME; + int SUCCESS_TIPS_LAST; + int FAILURE_TIPS_LAST; + int MAX_FACE_TRY_COUNT; + int MAX_FACE_NOT_FOUND_COUNT; + int MAX_IRIS_TRY_COUNT; + int MAX_EYE_NOT_FOUND_COUNT; + int MIN_EYE_SIZE; + + QString LOG_FILE; + QString LOG_LEVEL; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/id/IdWorker.h b/utils/id/IdWorker.h new file mode 100644 index 0000000..6c48aa9 --- /dev/null +++ b/utils/id/IdWorker.h @@ -0,0 +1,218 @@ +#ifndef _JW_CORE_ID_WORKER_H_ +#define _JW_CORE_ID_WORKER_H_ + +#include +#include +#include +#include +#include +#include "Noncopyable.h" +#include "Singleton.h" + +// 如果不使用 mutex, 则开启下面这个定义, 但是我发现, 还是开启 mutex 功能, 速度比较快 +// #define SNOWFLAKE_ID_WORKER_NO_LOCK + +namespace Jiawa { + + /** + * @brief 核心 + * 核心功能 + */ + namespace Core { + + /** + * @brief 分布式id生成类 + * https://segmentfault.com/a/1190000011282426 + * https://github.com/twitter/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala + * + * 64bit id: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 + * || || || | | | + * |└---------------------------时间戳--------------------------┘└中心-┘└机器-┘ └----序列号----┘ + * | + * 不用 + * SnowFlake的优点: 整体上按照时间自增排序, 并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分), 并且效率较高, 经测试, SnowFlake每秒能够产生26万ID左右. + */ + class SnowflakeIdWorker : private Noncopyable { + + // 实现单例 + friend class Singleton; + + public: + typedef unsigned int UInt; + typedef unsigned long long int UInt64; + +#ifdef SNOWFLAKE_ID_WORKER_NO_LOCK + typedef std::atomic AtomicUInt; + typedef std::atomic AtomicUInt64; +#else + typedef UInt AtomicUInt; + typedef UInt64 AtomicUInt64; +#endif + + void setWorkerId(UInt workerId) { + this->workerId = workerId; + } + + void setDatacenterId(UInt datacenterId) { + this->datacenterId = datacenterId; + } + + UInt64 getId() { + return nextId(); + } + + /** + * 获得下一个ID (该方法是线程安全的) + * + * @return SnowflakeId + */ + UInt64 nextId() { +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + std::unique_lock lock{ mutex }; + AtomicUInt64 timestamp{ 0 }; +#else + static AtomicUInt64 timestamp{ 0 }; +#endif + timestamp = timeGen(); + + // 如果当前时间小于上一次ID生成的时间戳, 说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + std::ostringstream s; + s << "clock moved backwards. Refusing to generate id for " << lastTimestamp - timestamp << " milliseconds"; + throw std::exception(std::runtime_error(s.str())); + } + + if (lastTimestamp == timestamp) { + // 如果是同一时间生成的, 则进行毫秒内序列 + sequence = (sequence + 1) & sequenceMask; + if (0 == sequence) { + // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0; + } + +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + lastTimestamp = timestamp; +#else + lastTimestamp = timestamp.load(); +#endif + + // 移位并通过或运算拼到一起组成64位的ID + return ((timestamp - twepoch) << timestampLeftShift) + | (datacenterId << datacenterIdShift) + | (workerId << workerIdShift) + | sequence; + } + + protected: + SnowflakeIdWorker() : workerId(0), datacenterId(0), sequence(0), lastTimestamp(0) { } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + UInt64 timeGen() const { + auto t = std::chrono::time_point_cast(std::chrono::high_resolution_clock::now()); + return t.time_since_epoch().count(); + } + + /** + * 阻塞到下一个毫秒, 直到获得新的时间戳 + * + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + UInt64 tilNextMillis(UInt64 lastTimestamp) const { + UInt64 timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + private: + +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + std::mutex mutex; +#endif + + /** + * 开始时间截 (2018-01-01 00:00:00.000) + */ + const UInt64 twepoch = 1514736000000; + + /** + * 机器id所占的位数 + */ + const UInt workerIdBits = 5; + + /** + * 数据中心id所占的位数 + */ + const UInt datacenterIdBits = 5; + + /** + * 序列所占的位数 + */ + const UInt sequenceBits = 12; + + /** + * 机器ID向左移12位 + */ + const UInt workerIdShift = sequenceBits; + + /** + * 数据标识id向左移17位 + */ + const UInt datacenterIdShift = workerIdShift + workerIdBits; + + /** + * 时间截向左移22位 + */ + const UInt timestampLeftShift = datacenterIdShift + datacenterIdBits; + + /** + * 支持的最大机器id, 结果是31 + */ + const UInt maxWorkerId = -1 ^ (-1 << workerIdBits); + + /** + * 支持的最大数据中心id, 结果是31 + */ + const UInt maxDatacenterId = -1 ^ (-1 << datacenterIdBits); + + /** + * 生成序列的掩码, 这里为4095 + */ + const UInt sequenceMask = -1 ^ (-1 << sequenceBits); + + /** + * 工作机器id(0~31) + */ + UInt workerId; + + /** + * 数据中心id(0~31) + */ + UInt datacenterId; + + /** + * 毫秒内序列(0~4095) + */ + AtomicUInt sequence{ 0 }; + + /** + * 上次生成ID的时间截 + */ + AtomicUInt64 lastTimestamp{ 0 }; + + }; + + typedef SnowflakeIdWorker IdWorker; + } +} + +#endif // _JW_CORE_ID_WORKER_H_ diff --git a/utils/id/Noncopyable.h b/utils/id/Noncopyable.h new file mode 100644 index 0000000..d87f58a --- /dev/null +++ b/utils/id/Noncopyable.h @@ -0,0 +1,31 @@ +#ifndef _JW_CORE_NONCOPYABLE_H_ +#define _JW_CORE_NONCOPYABLE_H_ + + +// Private copy constructor and copy assignment ensure classes derived from +// class noncopyable cannot be copied. + +namespace Jiawa { + namespace Core { + + // protection from unintended ADL(Argument Dependent Lookup) + namespace Noncopyable_ { + + class Noncopyable + { + protected: + Noncopyable() = default; + ~Noncopyable() = default; + + Noncopyable(const Noncopyable&) = delete; + Noncopyable(const Noncopyable&&) = delete; + Noncopyable& operator=(const Noncopyable&) = delete; + Noncopyable& operator=(const Noncopyable&&) = delete; + }; + } + + typedef Noncopyable_::Noncopyable Noncopyable; + } +} + +#endif // _JW_CORE_NONCOPYABLE_H_ diff --git a/utils/id/Singleton.h b/utils/id/Singleton.h new file mode 100644 index 0000000..d1c0553 --- /dev/null +++ b/utils/id/Singleton.h @@ -0,0 +1,60 @@ +#ifndef _JW_CORE_SINGLETON_H_ +#define _JW_CORE_SINGLETON_H_ + +namespace Jiawa { + namespace Core { + + // boost/container/detail/singleton.hpp + // http://blog.csdn.net/fullsail/article/details/8483106 + // T must be: no-throw default constructible and no-throw destructible + template + class Singleton { + private: + Singleton() = default; + ~Singleton() = default; + + private: + struct object_creator + { + // This constructor does nothing more than ensure that instance() + // is called before main() begins, thus creating the static + // T object before multithreading race issues can come up. + object_creator() { Singleton::instance(); } + inline void do_nothing() const { } + }; + static object_creator create_object; + + private: + Singleton(const Singleton&) = delete; + Singleton(const Singleton&&) = delete; + Singleton& operator=(const Singleton&) = delete; + Singleton& operator=(const Singleton&&) = delete; + + public: + typedef T object_type; + + // If, at any point (in user code), Singleton::instance() + // is called, then the following function is instantiated. + static object_type& instance() + { + // This is the object that we return a reference to. + // It is guaranteed to be created before main() begins because of + // the next line. + static object_type obj; + + // The following line does nothing else than force the instantiation + // of Singleton::create_object, whose constructor is + // called before main() begins. + create_object.do_nothing(); + + return obj; + } + }; + + template + typename Singleton::object_creator Singleton::create_object; + } +} + +#endif // _JW_CORE_SINGLETON_H_ + diff --git a/utils/id/Timer.h b/utils/id/Timer.h new file mode 100644 index 0000000..0f1ff1b --- /dev/null +++ b/utils/id/Timer.h @@ -0,0 +1,57 @@ +#ifndef _JW_CORE_TIMER_H_ +#define _JW_CORE_TIMER_H_ + +#include + +namespace Jiawa { + + /** + * @brief 核心 + * 核心功能 + */ + namespace Core { + + /** + * @brief 计时器类 + * 计时 + */ + template + class Timer { + public: + + typedef CLOCK clock; + typedef typename CLOCK::rep rep; + typedef typename CLOCK::duration duration; + typedef typename CLOCK::time_point time_point; + + Timer() : m_start(CLOCK::now()) { + } + + /** + * @brief 重置计时器 + * + * @return 开始的时间点 + */ + time_point reset() { + m_start = CLOCK::now(); + return m_start; + } + + /** + * @brief 得到流逝的时间 + * @param UNIT 返回时间的类型 + * + * @return 返回流逝的时间 + */ + template + typename UNIT::duration::rep elapsed() const { + return std::chrono::duration_cast(CLOCK::now() - m_start).count(); + } + + private: + time_point m_start; + }; + } +} + +#endif // _JW_CORE_TIMER_H_ \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/AppendIrisCodeUtils.pro b/AppendIrisCodeUtils.pro new file mode 100644 index 0000000..e690848 --- /dev/null +++ b/AppendIrisCodeUtils.pro @@ -0,0 +1,45 @@ +QT += core gui sql + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include("casic/casic.pri") +include("dao/dao.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += MainWindow.cpp +SOURCES += PersonListForm.cpp + +HEADERS += MainWindow.h +HEADERS += PersonListForm.h + +FORMS += MainWindow.ui +FORMS += PersonListForm.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420 +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lopencv_world420d +else:unix:!macx: LIBS += -L$$PWD/lib/ -lopencv_world420 + +INCLUDEPATH += $$PWD/include +DEPENDPATH += $$PWD/include + +INCLUDEPATH += $$PWD/Eigen + diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..ebd1392 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,164 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +#include "dao/IrisDataDao.h" +#include "casic/iris/CasicIrisInfo.h" + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&personForm, &PersonListForm::personRecordSelected, this, &MainWindow::onPersonSelectedHandler); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_btnPerson_clicked() +{ + personForm.setWindowModality(Qt::WindowModality::ApplicationModal); + personForm.initPersonListDialog(); + personForm.show(); +} + +void MainWindow::on_btnLeftCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftCode->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelLeftMask->setText(selectedFile); + } +} + +void MainWindow::on_btnRightCode_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightCode->setText(selectedFile); + } +} + +void MainWindow::on_btnRightMask_clicked() +{ + QString selectedFile = QFileDialog::getOpenFileName(this, "选择文件", QDir::currentPath()); + if (selectedFile.isEmpty() == false) + { + ui->labelRightMask->setText(selectedFile); + } +} + +void MainWindow::on_btnLeftIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::on_btnRightIris_clicked() +{ + QMessageBox::information(this, "提示", "敬请期待"); +} + +void MainWindow::onPersonSelectedHandler(QString personId, QString name, QString deptName) +{ + ui->labelPersonId->setText(personId); + ui->labelName->setText(name); + ui->labelDept->setText(deptName); + + personForm.hide(); +} + +void MainWindow::on_btnSubmit_clicked() +{ + if (ui->labelPersonId->text() == "") { + QMessageBox::critical(this, "错误", "人员必须选择"); + return; + } + + if (ui->labelLeftCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜编码图像"); + return; + } + + if (ui->labelLeftMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择左眼虹膜掩码图像"); + return; + } + + if (ui->labelRightCode->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜编码图像"); + return; + } + + if (ui->labelRightMask->text() == "") { + QMessageBox::critical(this, "错误", "必须选择右眼虹膜掩码图像"); + return; + } + + IrisDataDao irisDao; + + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = ui->labelPersonId->text(); + leftFeature.irisCode = cv::imread(std::string(ui->labelLeftCode->text().toLocal8Bit()), 0); + leftFeature.maskNorm = cv::imread(std::string(ui->labelLeftMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo; + irisInfo.irisCode = leftFeature.irisCode; + irisInfo.maskNorm = leftFeature.maskNorm; + leftFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = ui->labelPersonId->text(); + rightFeature.irisCode = cv::imread(std::string(ui->labelRightCode->text().toLocal8Bit()), 0); + rightFeature.maskNorm = cv::imread(std::string(ui->labelRightMask->text().toLocal8Bit()), 0); + + CasicIrisInfo irisInfo2; + irisInfo2.irisCode = rightFeature.irisCode; + irisInfo2.maskNorm = rightFeature.maskNorm; + rightFeature.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo2); + + QVariantMap obj; + obj.insert("person_id", ui->labelPersonId->text()); + obj.insert("left_iris_code1", leftFeature.irisFeatureCode); + obj.insert("right_iris_code1", rightFeature.irisFeatureCode); + QString succId = irisDao.save(obj); + if (succId != "-1") { + QMessageBox::information(this, "成功", "保存人员虹膜特征码成功"); + + ui->labelName->setText(""); + ui->labelDept->setText(""); + ui->labelPersonId->setText(""); + ui->labelLeftCode->setText(""); + ui->labelLeftMask->setText(""); + ui->labelRightCode->setText(""); + ui->labelRightMask->setText(""); + } else { + QMessageBox::critical(this, "错误", "保存人员虹膜特征码失败"); + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..f9ff36c --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "PersonListForm.h" + +#include "casic/iris/CasicIrisInterface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_btnPerson_clicked(); + void on_btnLeftCode_clicked(); + void on_btnLeftMask_clicked(); + void on_btnRightCode_clicked(); + void on_btnRightMask_clicked(); + void on_btnLeftIris_clicked(); + void on_btnRightIris_clicked(); + void on_btnSubmit_clicked(); + void onPersonSelectedHandler(QString personId, QString name, QString deptName); + +private: + Ui::MainWindow *ui; + + PersonListForm personForm; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..3308361 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,423 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + 虹膜数据导入工具 + + + + + 10 + + + 40 + + + 20 + + + 20 + + + 20 + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 选择人员 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼编码 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼Mask + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 左眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 30 + + + + 右眼图像 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 40 + + + + 保存 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/PersonListForm.cpp b/PersonListForm.cpp new file mode 100644 index 0000000..30bc33d --- /dev/null +++ b/PersonListForm.cpp @@ -0,0 +1,150 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + + +#include "PersonListForm.h" +#include "ui_PersonListForm.h" + +#include + +PersonListForm::PersonListForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::PersonListForm) +{ + ui->setupUi(this); + + // 1. 初始化 QTableView + QStringList tbLabels; + tbLabels << "人员Id" << "姓名" << "部门" << ""; + + QStandardItemModel * tbModel = new QStandardItemModel(this); + tbModel->setHorizontalHeaderLabels(tbLabels); + + // 项选择模型 + QItemSelectionModel * oldModel = ui->tableView->selectionModel(); // We recommend that you delete the old selection model if it is no longer required. + ui->tableView->setModel(tbModel); + if (oldModel != nullptr) + { + oldModel->deleteLater(); // 手动删除 + } + + // 调用 setColumnWidth() 设置列宽 ---- 在 setModel() 之后 + ui->tableView->setColumnWidth(0, 200); + ui->tableView->setColumnWidth(1, 180); + ui->tableView->setColumnWidth(2, 160); +} + +PersonListForm::~PersonListForm() +{ + delete ui; +} + +void PersonListForm::initPersonListDialog() +{ + current = 1; + caculatePage(); + + // 最后,查询数据库,并在界面显示 + loadPersonListFromDatabase(); +} + +void PersonListForm::loadPersonListFromDatabase() +{ + QVector personList = personDao.findRecordsByNameAndDept(ui->inputName->text(), "", limit, offset); + + // 2. 界面表格显示 + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + if (tbModel == nullptr) + { + return; + } + + if (tbModel->rowCount() > 0) + { + tbModel->removeRows(0, tbModel->rowCount()); // 删除所有行 + } + + for (QVariantMap person : personList) { + QList row; + + QStandardItem * idItem = new QStandardItem(person.value("id").toString()); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setEditable(false); + row.append(idItem); + + QStandardItem * nameItem = new QStandardItem(person.value("name").toString()); + nameItem->setTextAlignment(Qt::AlignCenter); + nameItem->setEditable(false); + row.append(nameItem); + + QStandardItem * deptItem = new QStandardItem(person.value("deptname").toString()); + deptItem->setTextAlignment(Qt::AlignCenter); + deptItem->setEditable(false); + row.append(deptItem); + + tbModel->appendRow(row); + } +} + +void PersonListForm::caculatePage() +{ + quint16 total = personDao.findRecordCountByNameAndDept(ui->inputName->text(), ""); + + page = total % limit == 0 ? (total / limit) : (total / limit + 1); + offset = (current - 1) * limit; + + ui->labelPage->setText(QString("%1 / %2").arg(current).arg(page)); +} + +void PersonListForm::on_tableView_doubleClicked(const QModelIndex &index) +{ + int row = index.row(); + + QStandardItemModel * tbModel = dynamic_cast(ui->tableView->model()); // 获取 已绑定的数据模型 QStandardItemModel + + QList colsInRow = tbModel->takeRow(row); + + QString personId = colsInRow.at(0)->text(); + QString name = colsInRow.at(1)->text(); + QString deptName = colsInRow.at(2)->text(); + + emit personRecordSelected(personId, name, deptName); +} + +void PersonListForm::on_btnFirst_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnPrev_clicked() +{ + current -= 1; + if (current < 1) current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnNext_clicked() +{ + current += 1; + if (current > page) current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnLast_clicked() +{ + current = page; + caculatePage(); + loadPersonListFromDatabase(); +} + +void PersonListForm::on_btnQuery_clicked() +{ + current = 1; + caculatePage(); + loadPersonListFromDatabase(); +} diff --git a/PersonListForm.h b/PersonListForm.h new file mode 100644 index 0000000..b466dd1 --- /dev/null +++ b/PersonListForm.h @@ -0,0 +1,49 @@ +#ifndef PERSONLISTFORM_H +#define PERSONLISTFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" + +namespace Ui { +class PersonListForm; +} + +class PersonListForm : public QWidget +{ + Q_OBJECT + +public: + explicit PersonListForm(QWidget *parent = nullptr); + ~PersonListForm(); + + void initPersonListDialog(); + +private slots: + void on_tableView_doubleClicked(const QModelIndex &index); + void on_btnQuery_clicked(); + + void on_btnFirst_clicked(); + void on_btnPrev_clicked(); + void on_btnNext_clicked(); + void on_btnLast_clicked(); + +private: + Ui::PersonListForm *ui; + + SysPersonDao personDao; + + quint16 limit = 10; + quint16 offset = 0; + quint16 page = 0; + quint16 current = 1; + + void loadPersonListFromDatabase(); + void caculatePage(); + +signals: + void personRecordSelected(QString personId, QString name, QString deptName); +}; + +#endif // PERSONLISTFORM_H diff --git a/PersonListForm.ui b/PersonListForm.ui new file mode 100644 index 0000000..fd2178b --- /dev/null +++ b/PersonListForm.ui @@ -0,0 +1,117 @@ + + + PersonListForm + + + + 0 + 0 + 600 + 400 + + + + Form + + + + + 10 + 10 + 200 + 30 + + + + + + + 10 + 360 + 25 + 25 + + + + << + + + + + + 45 + 360 + 25 + 25 + + + + < + + + + + + 80 + 360 + 25 + 25 + + + + > + + + + + + 115 + 360 + 25 + 25 + + + + >> + + + + + + 10 + 50 + 580 + 300 + + + + + + + 180 + 360 + 60 + 25 + + + + + + + + + + 500 + 10 + 90 + 30 + + + + 查询 + + + + + + diff --git a/casic/casic.pri b/casic/casic.pri new file mode 100644 index 0000000..88263a7 --- /dev/null +++ b/casic/casic.pri @@ -0,0 +1,2 @@ +include(iris/casicIris.pri) + diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h new file mode 100644 index 0000000..e41ed26 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,35 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include "opencv2/opencv.hpp" +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // 眼部图像 + // 后续计算需要使用 + cv::Mat matData; // 原始图像 找到眼睛之后裁剪成640 * 480的灰度图 + + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + + // 字节数组形式的特征码 + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; +}; + +#endif // CASICIRISINFO_H diff --git a/casic/iris/CasicIrisInterface.cpp b/casic/iris/CasicIrisInterface.cpp new file mode 100644 index 0000000..c528381 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,241 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" + +#include +#include +#include + +casic::iris::CasicIrisInterface::CasicIrisInterface() +{ + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); +} + +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(); +// 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()); + qDebug() << 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); + + irisInfo.hasEye = true; +// irisInfo.eyeRect = rect.at(0); + +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::findAndCutEye(CasicIrisInfo irisInfo) +{ + // 构建OpenCV自带的眼睛分类器 + if (this->cascade == nullptr) { + this->cascade = new cv::CascadeClassifier(); + this->cascade->load(cascadeName); + } + + 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(); +// 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()); + qDebug() << QString("眼睛检测算法[count: %1][rect: (%2, %3) %4 * %5]") + .arg(rect.size()) + .arg(rect.at(0).x).arg(rect.at(0).y) + .arg(rect.at(0).width).arg(rect.at(0).height); + + // 找到眼睛之后, 将眼部裁剪下来, 图像拉伸到640 * 480 + cv::Point center; + center.x = cvFloor(rect.at(0).x + rect.at(0).width*0.5); + center.y = cvFloor(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; + 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 cutSize = irisInfo.matData.clone(); + cutSize = 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)); + + // 裁剪后放大到 640 * 480 + cv::resize(cutSize, cutSize, cv::Size(SettingConfig::getInstance().IRIS_WIDTH, SettingConfig::getInstance().IRIS_HEIGHT)); + cv::flip(cutSize, cutSize, 1); // 左右翻转 + + irisInfo.hasEye = true; + irisInfo.matData = cutSize; + + 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; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + + cv::resize(irisInfo.segResult.irisMask, irisInfo.segResult.irisMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.innerMask, irisInfo.segResult.innerMask, cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + cv::resize(irisInfo.segResult.outerCircle, irisInfo.segResult.outerCircle,cv::Size(irisInfo.matData.cols, irisInfo.matData.rows)); + + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } +// std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +QByteArray casic::iris::CasicIrisInterface::extractFeature(CasicIrisInfo irisInfo) +{ + QByteArray irisFeatureCode = rec.extractFeature(irisInfo.irisCode, irisInfo.maskNorm); + return irisFeatureCode; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} + +float casic::iris::CasicIrisInterface::calculatePairPoints(QByteArray feature, QByteArray other) +{ + return rec.matchFeatureCode(reinterpret_cast(feature.data()), reinterpret_cast(other.data())); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h new file mode 100644 index 0000000..26fbedc --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,55 @@ +#ifndef CASICIRISINTERFACE_H +#define CASICIRISINTERFACE_H + +#include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.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 findAndCutEye(CasicIrisInfo irisInfo); + CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); + CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); + + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + QByteArray extractFeature(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); + float calculatePairPoints(QByteArray feature, QByteArray other); + + void setCascadeFile(QString filename); + void setMinEyeSize(int minEyeSize); + void setMaxEyeSize(int maxEyeSize); + + private: + CasicIrisInterface(); + + std::string cascadeName = "./model/haarcascade_eye.xml"; + const char * gaborFilterFileName = "./model/filters.txt"; + const char * applicationPointsFileName = "./model/points.txt"; + int minEyeSize = 320; + int maxEyeSize = 480; + float cutRatio = 0.72f; // 裁剪比率 用于裁剪更大比例的眼部图像 + + cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; + }; + } +} + + +#endif // CASICIRISINTERFACE_H diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..ad456b4 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,488 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + mvApplicationPoints.push_back(QPair(i, j)); + + // Set pixel to "on" + if (i < 0 || i > applicationPoints.rows - 1 || j < 0 || j > applicationPoints.cols- 1) + { + std::cout << "Point (" << i << "," << j << ") "; + std::cout << "exceeds size of normalized image : "; + std::cout << applicationPoints.rows << "x" << applicationPoints.cols; + std::cout << " while loading application points" << std::endl; + } + else + { + applicationPoints.at(i,j) = 255; + } + } + + //cv::imshow("mpApplicationPoints", mpApplicationPoints); + //cv::waitKey(0); + + // Close the file + file.close(); + + return applicationPoints; +} + +cv::Mat CasicIrisRec::encodeToImage(cv::Mat normalizedImage) +{ + cv::Size size(normalizedImage.size()); + cv::Mat encodeImage(cv::Size(size.width, size.height * gaborFilters.size()), CV_32FC1); + + int max_width = 0; + for (int f = 0; f < gaborFilters.size(); f++) + if (gaborFilters[f].cols > max_width) + max_width = gaborFilters[f].cols; + max_width = (max_width - 1) / 2; + + cv::Mat resized = addBorders(normalizedImage, max_width); + +// std::cout << resized.size() << std::endl; +// cv::imshow("resized", resized); +// cv::waitKey(0); + + cv::Mat img1(resized.size(), CV_32F, 1); + cv::Mat img2(resized.size(), encodeImage.depth(), 1); + + for (int f = 0; f < gaborFilters.size(); f++) + { + // Convolution + cv::filter2D(resized, img1, img1.depth(), gaborFilters[f]); + + // Threshold : above or below 0 + cv::threshold(img1, img2, 0, 255, cv::THRESH_BINARY); + +// cv::imshow("img2", img2); +// cv::waitKey(0); + + // Form the iris code + cv::Mat roi = img2(cv::Rect(max_width, 0, normalizedImage.cols, normalizedImage.rows)); + cv::Mat dst = encodeImage(cv::Rect(0, f*normalizedImage.rows, normalizedImage.cols, normalizedImage.rows)); + roi.copyTo(dst); + //cv::copyTo(img2, dst, NULL); + } + cv::Mat image8UC1; + encodeImage.convertTo(image8UC1, CV_8UC1); + return image8UC1; +} + +QByteArray CasicIrisRec::extractFeature(cv::Mat irisCode, cv::Mat maskNorm) +{ + // count points width and height + cv::Mat pointTemp(applicationPoints); + cv::Mat rowTemp; + + std::vector pointRows; + for (int i = 0; i < applicationPoints.rows; i++) { + rowTemp = pointTemp(cv::Rect(0, i, applicationPoints.cols, 1)); + if (cv::sum(rowTemp)[0] > 0) { + pointRows.push_back(i); + } + } + + int width = irisCode.cols; + int height = pointRows.size(); + rowTemp.release(); + pointTemp.release(); + + int n_codes = irisCode.rows / applicationPoints.rows; + + int irisDataLength = (n_codes + 1) * width * height; + uchar * irisData = new uchar[irisDataLength]; + int irisDataIndex = 0; + + uchar* codePixel = new uchar; + + // save iris code 512 * 8 * 6 + for (int n = 0; n < n_codes; n++) { // 6 + for (auto i : pointRows) { // 8 + i = n * maskNorm.rows + i; + for (int j = 0; j < irisCode.cols; j++) { // 512 + codePixel = (uchar*)(irisCode.data + i * irisCode.step + j); + if (*codePixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *codePixel; + } + } + } + } + + // save iris mask 512 * 8 * 1 + uchar * maskPixel; + for (auto i : pointRows) { // 8 + for (int j = 0; j < irisCode.cols; j++) { // 512 + maskPixel = (uchar*)(maskNorm.data + i * maskNorm.step + j); + if (*maskPixel == 255) { + irisData[irisDataIndex++] = 1; + } + else { + irisData[irisDataIndex++] = *maskPixel; + } + } + } + + + uchar * irisFeaturePtr = new uchar[irisDataLength / 8 + 5]; + memset(irisFeaturePtr, 0, irisDataLength / 8 + 5); + + // first byte is width; + irisFeaturePtr[0] = (uchar)width; + irisFeaturePtr[1] = (uchar)(width >> 8); + + // second byte is height; + irisFeaturePtr[2] = (uchar)height; + + // third byte is n_codes + irisFeaturePtr[3] = (uchar)n_codes; + + // fourth byte is n_mask(1) + irisFeaturePtr[4] = (uchar)1; + for (int i = 0; i < irisDataLength; i++) { + irisFeaturePtr[5 + i / 8] += irisData[i] << (7 - (i % 8)); + } + + QByteArray feature; + for (int i = 0; i < irisDataLength / 8 + 5; i++) { + feature.append((char) irisFeaturePtr[i]); + } + + delete[] irisData; + delete[] irisFeaturePtr; + return feature; +} + +float CasicIrisRec::matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2) +{ + code1.convertTo(code1, CV_8UC1); + code2.convertTo(code2, CV_8UC1); + + cv::Mat temp(applicationPoints.size(), CV_8UC1); + temp.setTo(cv::Scalar(0)); + + cv::bitwise_and(normalizedMask1, normalizedMask2, temp, applicationPoints); + + // Copy the mask f times, where f correspond to the number of codes (= number of filters) + int n_codes = code1.rows / applicationPoints.rows; + + cv::Mat totalMask(code1.size(), CV_8UC1); + for (int n = 0; n < n_codes; n++) { + cv::Mat maskRoi = totalMask(cv::Rect(0, n*applicationPoints.rows, applicationPoints.cols, applicationPoints.rows)); + temp.copyTo(maskRoi); + } + +// cv::imshow("totalMask", totalMask); +// cv::waitKey(0); + + cv::Mat result(code1.size(), CV_8UC1); + result.setTo(cv::Scalar(0)); + + int shift = 10; + cv::Mat shifted = addBorders(code1, shift); + + float score = 1; + for (int s = -shift; s <= shift; s++) { + cv::Mat roi = shifted(cv::Rect(shift + s, 0, code1.cols, code1.rows)); + cv::bitwise_xor(roi, code2, result, totalMask); + + float mean = (cv::sum(result).val[0]) / (cv::sum(totalMask).val[0]); + // std::cout << "mean: " << mean << std::endl; + score = std::min(score, mean); + } + + // std::cout << "score: " << score << std::endl; + return score; + +} + +float CasicIrisRec::matchFeatureCode(uchar *feature1, uchar *feature2) +{ + int width = feature1[0] | feature1[1] << 8; // 32 + int height = feature1[2]; // 8 + int nCode = feature1[3]; // 6 +// int nMask = feature1[4]; + + uchar ** irisData1 = reduceIrisCode(feature1); + uchar ** irisMask1 = reduceIrisMask(feature1); + uchar ** irisData2 = reduceIrisCode(feature2); + uchar ** irisMask2 = reduceIrisMask(feature2); + + int shift = 10; + float score = 1.0; + + int minstep = 0 - shift; + int maxstep = shift; + + for (int s = minstep; s <= maxstep; s++) { + int count = 0; + float mean = 0.0; + for (int n = 0; n < nCode; n++) { + for (auto pos : mvApplicationPoints) { + int row = pos.first / mvApplicationPoints[0].first - 1; + int col = (pos.second + s + width) % width; + + if (irisMask1[row][pos.second] == 1 && irisMask2[row][pos.second] == 1) { + + uchar tmp1 = irisData1[row + height * n][col]; + uchar tmp2 = irisData2[row + height * n][pos.second]; + mean += tmp1 ^ tmp2; + count += 1; + } + } + } + + //cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << endl; + mean = mean / count; + score = mean < score ? mean : score; + + } + +// std::cout << "feature score: " << score << std::endl; + + +// for (int i = 0; i < height * nCode; i++) { +// delete irisData1[i]; +// delete irisData2[i]; +// } +// delete irisData1; +// delete irisData2; + +// for (int i = 0; i < height; i++) { +// delete irisMask1[i]; +// delete irisMask2[i]; +// } +// delete irisMask1; +// delete irisMask2; + return score; +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +uchar ** CasicIrisRec::reduceIrisCode(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2] * feature[3]; + + uchar ** reduceCode = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + reduceCode[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = i * width + j; + reduceCode[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return reduceCode; +} + +uchar ** CasicIrisRec::reduceIrisMask(uchar *feature) +{ + int width = feature[0] | feature[1] << 8; + int height = feature[2]; // 8 + int nCode = feature[3]; // 6 + + uchar ** irisMask = new uchar*[width*height]; + for (int i = 0; i < height; i++) { + irisMask[i] = new uchar[width]; + for (int j = 0; j < width; j++) { + int tmp = width * height * nCode + i * width + j; + irisMask[i][j] = (uchar)feature[5 + tmp / 8] >> (7 - tmp % 8) & 1; + } + } + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..2d9616e --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,47 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#include +#include +#include + +namespace iristrt +{ + +class CasicIrisRec +{ + +public: + CasicIrisRec(); + CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName); + ~CasicIrisRec(); + + cv::Mat normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r); + cv::Mat encodeToImage(cv::Mat normalizedImage); + QByteArray extractFeature(cv::Mat irisCode, cv::Mat maskNorm); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + float matchFeatureCode(uchar * feature1, uchar * feature2); + +private: + + std::vector gaborFilters; + cv::Mat applicationPoints; + std::vector> mvApplicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + uchar ** reduceIrisCode(uchar * feature); + uchar ** reduceIrisMask(uchar * feature); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicIrisUtil.cpp b/casic/iris/CasicIrisUtil.cpp new file mode 100644 index 0000000..d72168a --- /dev/null +++ b/casic/iris/CasicIrisUtil.cpp @@ -0,0 +1,44 @@ +#include "CasicIrisUtil.h" + + +namespace iristrt +{ + +std::vector split(std::string s,char token){ + std::istringstream iss(s); + std::string word; + std::vector vs; + while(getline(iss,word,token)){ + if(word.length() <= 0){ + continue; + } + vs.push_back(word); + } + return vs; +} + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ) +{ + cv::Mat res = image.clone(); + cv::circle(res,cv::Point(iris_x,iris_y),iris_r,0,3); + cv::circle(res,cv::Point(pupil_x,pupil_y),pupil_r,0,3); + + cv::imshow("mask",mask); + cv::imshow("res",res); + cv::waitKey(0); + cv::destroyAllWindows(); +} + +float sigmoid(float x) +{ + return (1 / (1 + exp(-x))); +} + +int point_distance(int x1,int y1,int x2, int y2) +{ + int x_power = pow(x1-x2,2); + int y_power = pow(y1-y2,2); + return (int)sqrt(x_power + y_power); +} + +} diff --git a/casic/iris/CasicIrisUtil.h b/casic/iris/CasicIrisUtil.h new file mode 100644 index 0000000..2053316 --- /dev/null +++ b/casic/iris/CasicIrisUtil.h @@ -0,0 +1,26 @@ +#ifndef CASIC_IRIS_UTIL +#define CASIC_IRIS_UTIL + +#include +#include +#include +#include +//#include +#include +#include + +namespace iristrt +{ + +std::vector split(std::string s,char token); + +void showResult(cv::Mat image,cv::Mat mask, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r ); + +float sigmoid(float x); + +int point_distance(int x1,int y1,int x2, int y2); + + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegPostProcess.cpp b/casic/iris/CasicSegPostProcess.cpp new file mode 100644 index 0000000..740beb7 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask, CV_8UC1); + iris.convertTo(iris, CV_8UC1); + pupil.convertTo(pupil, CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + +// cv::Mat three_mask = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC3); +// std::vector channels_m; +// for (int i = 0; i < 3; i++) +// { +// channels_m.push_back(mask); +// } +// merge(channels_m, three_mask); + +// circle(three_mask, cv::Point(iris_x, iris_y), iris_r, cv::Scalar(0, 0, 255), 1); +// circle(three_mask, cv::Point(pupil_x, pupil_y), pupil_r, cv::Scalar(0, 0, 255), 1); +// cv::imshow("three_mask", three_mask); +// cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri new file mode 100644 index 0000000..43a1bd3 --- /dev/null +++ b/casic/iris/casicIris.pri @@ -0,0 +1,12 @@ +HEADERS += $$PWD/CasicIrisInfo.h +HEADERS += $$PWD/CasicIrisInterface.h + +HEADERS += $$PWD/CasicSegResult.h +HEADERS += $$PWD/CasicSegPostProcess.h +HEADERS += $$PWD/CasicIrisUtil.h +HEADERS += $$PWD/CasicIrisRec.h + +SOURCES += $$PWD/CasicIrisInterface.cpp +SOURCES += $$PWD/CasicSegPostProcess.cpp +SOURCES += $$PWD/CasicIrisUtil.cpp +SOURCES += $$PWD/CasicIrisRec.cpp diff --git a/dao/BaseDao.cpp b/dao/BaseDao.cpp new file mode 100644 index 0000000..f51c144 --- /dev/null +++ b/dao/BaseDao.cpp @@ -0,0 +1,12 @@ +#include "BaseDao.h" +#include "dao/util/ConnectionManager.h" + +BaseDao::BaseDao(QObject *parent) : QObject(parent) +{ + +} + +BaseDao::~BaseDao() +{ + +} diff --git a/dao/BaseDao.h b/dao/BaseDao.h new file mode 100644 index 0000000..5fa355f --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,33 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#include +#include +#include +#include +#include + +#include "dao/util/ConnectionManager.h" +//#include "utils/UtilInclude.h" + +class BaseDao : public QObject +{ + Q_OBJECT +public: + explicit BaseDao(QObject *parent = nullptr); + ~BaseDao(); + + virtual QVector findAllRecord() = 0; + virtual QVariantMap findRecordById(QString id) = 0; + + virtual QString save(QVariantMap object) = 0; + virtual bool edit(QVariantMap newObject, QString id) = 0; + virtual bool dele(QString id) = 0; + +private: + +signals: + +}; + +#endif // BASEDAO_H diff --git a/dao/IrisDataDao.cpp b/dao/IrisDataDao.cpp new file mode 100644 index 0000000..214f648 --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,238 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisDataDao.h" + +IrisDataDao::IrisDataDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector IrisDataDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM IRIS_DATA"; + query.prepare(sql); + + // 执行查询 + query.exec(); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + 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")); + item.insert("left_iris_code3", query.value("left_iris_code3")); + item.insert("right_iris_code1", query.value("right_iris_code1")); + item.insert("right_iris_code2", query.value("right_iris_code2")); + item.insert("right_iris_code3", query.value("right_iris_code3")); + + result.append(item); + } + +// LOG_DEBUG(QString("查询IRIS_DATA表的所有记录[结果数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + 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表的记录[id=%1]").arg(id).toStdString()); + return result; +} + +QVariantMap IrisDataDao::findRecordByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + 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]").arg(personId).toStdString()); + return result; +} + +QString IrisDataDao::findIrisIdByPersonId(QString personId) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT ID FROM IRIS_DATA WHERE PERSON_ID = :personId"); + query.prepare(sql); + query.bindValue(":personId", personId); + + // 执行查询 + query.exec(); + + // 返回结果 + QString existedId; + + // 遍历查询结果 + if (query.next()) { + existedId = query.value("id").toString(); + } + + return existedId; +} + + +QString IrisDataDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id; + if (object.contains("id") == true) { + id = object.value("id").toULongLong(); + } else { + id = ConnectionManager::getInstance()->generateId(); + } + + // INSERT语句 + QString sql = QString("INSERT INTO IRIS_DATA (ID, PERSON_ID, ID_CARD_NO, LEFT_IRIS_CODE1, RIGHT_IRIS_CODE1) VALUES (:id, :personId, :idCardNo, :left1, :right1)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":idCardNo", object.value("id_card_no").toString()); + query.bindValue(":left1", object.value("left_iris_code1")); + query.bindValue(":right1", object.value("right_iris_code1")); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + +// LOG_DEBUG(QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + qDebug() << QString("保存虹膜特征值[%1][id=%2]").arg(success).arg(id); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool IrisDataDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + 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 = :id"); + query.prepare(sql); + query.bindValue(":id", id); + 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][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} + + +bool IrisDataDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString sql = QString("DELETE FROM IRIS_DATA WHERE ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(); + +// LOG_DEBUG(QString("删除虹膜特征值[%1][id=%2]").arg(success).arg(id).toStdString()); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + return success; +} diff --git a/dao/IrisDataDao.h b/dao/IrisDataDao.h new file mode 100644 index 0000000..ce2911f --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,26 @@ +#ifndef IRISDATADAO_H +#define IRISDATADAO_H + +#include +#include "BaseDao.h" + +class IrisDataDao : public BaseDao +{ + Q_OBJECT +public: + explicit IrisDataDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + QVariantMap findRecordByPersonId(QString personId); + QString findIrisIdByPersonId(QString personId); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/dao/RecognitionRecordsDao.cpp b/dao/RecognitionRecordsDao.cpp new file mode 100644 index 0000000..d795039 --- /dev/null +++ b/dao/RecognitionRecordsDao.cpp @@ -0,0 +1,98 @@ +#include "RecognitionRecordsDao.h" + +RecognitionRecordsDao::RecognitionRecordsDao(QObject *parent) : BaseDao(parent) +{ + +} + + +QVector RecognitionRecordsDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM RECOGNITION_RECORDS"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + result.append(item); + } + +// LOG_DEBUG(QString("查询RECOGNITION_RECORDS表的所有记录[记录数:%1]").arg(result.size()).toStdString()); + return result; +} + +QVariantMap RecognitionRecordsDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + +QString RecognitionRecordsDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + + // INSERT语句 + QString sql = QString("INSERT INTO RECOGNITION_RECORDS (ID, PERSON_ID, DATETIME, REC_TYPE, DEV_CODE, DOOR_CODE, INOUT_TYPE) " + "VALUES (:id, :personId, :datetime, :recType, :devCode, :doorCode, :inoutType)"); + + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":personId", object.value("person_id").toString()); + query.bindValue(":datetime", object.value("date_time").toString()); + query.bindValue(":recType", object.value("rec_type").toString()); + query.bindValue(":devCode", object.value("dev_code").toString()); + query.bindValue(":doorCode", object.value("door_code").toString()); + query.bindValue(":inoutType", object.value("inout_type").toString()); + + // 插入识别的log日志 + QString logStr = QString("INSERT INTO RECOGNITION_LOG (ID, LOG_INFO) VALUES (:id, :logInfo)"); + + QSqlQuery logQuery(ConnectionManager::getInstance()->getConnection()); + logQuery.prepare(logStr); + logQuery.bindValue(":id", id); + logQuery.bindValue(":logInfo", object.value("debug_info").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(); + logQuery.exec(); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool RecognitionRecordsDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool RecognitionRecordsDao::dele(QString id) +{ + return false; +} diff --git a/dao/RecognitionRecordsDao.h b/dao/RecognitionRecordsDao.h new file mode 100644 index 0000000..38de7c9 --- /dev/null +++ b/dao/RecognitionRecordsDao.h @@ -0,0 +1,23 @@ +#ifndef RECOGNITIONRECORDSDAO_H +#define RECOGNITIONRECORDSDAO_H + +#include +#include "BaseDao.h" +class RecognitionRecordsDao: public BaseDao +{ + Q_OBJECT +public: + explicit RecognitionRecordsDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // RECOGNITIONRECORDSDAO_H diff --git a/dao/SysDeptDao.cpp b/dao/SysDeptDao.cpp new file mode 100644 index 0000000..22f9946 --- /dev/null +++ b/dao/SysDeptDao.cpp @@ -0,0 +1,88 @@ +#include "SysDeptDao.h" + +SysDeptDao::SysDeptDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDeptDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DEPT WHERE PID != '-1' ORDER BY PID, NUM"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + item.insert("id", query.value("id").toString()); + item.insert("pid", query.value("pid").toString()); + item.insert("pids", query.value("pids").toString()); + item.insert("simplename", query.value("simplename").toString()); + item.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("查询表[SYS_DEPT]的所有记录[%1][%2]").arg(result.size()).arg(sql).toStdString()); + + return result; +} + +QVariantMap SysDeptDao::findRecordById(QString id) +{ + QVariantMap item; + return item; + + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_DEPT WHERE SYS_DEPT.ID = :id"); + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("pid", query.value("pid").toString()); + result.insert("pids", query.value("pids").toString()); + result.insert("simplename", query.value("simplename").toString()); + result.insert("fullname", query.value("fullname").toString()); + } + +// LOG_TRACE(QString("根据id查询SYS_DEPT表的记录[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + + +QString SysDeptDao::save(QVariantMap object) +{ + return "0"; +} +bool SysDeptDao::edit(QVariantMap newObject, QString id) +{ + return true; +} +bool SysDeptDao::dele(QString id) +{ + return true; +} diff --git a/dao/SysDeptDao.h b/dao/SysDeptDao.h new file mode 100644 index 0000000..e07a09f --- /dev/null +++ b/dao/SysDeptDao.h @@ -0,0 +1,24 @@ +#ifndef SYSDEPTDAO_H +#define SYSDEPTDAO_H + +#include +#include "BaseDao.h" + +class SysDeptDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDeptDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +private: + +}; + +#endif // SYSDEPTDAO_H diff --git a/dao/SysDictDao.cpp b/dao/SysDictDao.cpp new file mode 100644 index 0000000..7a2bc8b --- /dev/null +++ b/dao/SysDictDao.cpp @@ -0,0 +1,82 @@ +#include "SysDictDao.h" + + +SysDictDao::SysDictDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysDictDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT"; + + // 执行查询 + query.exec(sql); + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + item.insert("id", query.value("id").toLongLong()); + item.insert("code", query.value("code").toString()); + item.insert("name", query.value("name").toString()); + item.insert("pid", query.value("pid").toString()); + + result.append(item); + } + + return result; +} + +QVariantMap SysDictDao::findRecordById(QString id) +{ + QVariantMap item; + return item; +} + + +QVariantMap SysDictDao::findChildDictByCode(QString code) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_DICT where PID = (SELECT ID FROM SYS_DICT WHERE CODE = :code)"; + query.prepare(sql); + query.bindValue(":code", code); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 遍历查询结果 + while (query.next()) { + result.insert(query.value("code").toString(), query.value("name").toString()); + } + + return result; +} + +QString SysDictDao::save(QVariantMap object) +{ + return "0"; +} + +bool SysDictDao::edit(QVariantMap newObject, QString id) +{ + return false; +} + +bool SysDictDao::dele(QString id) +{ + return false; +} diff --git a/dao/SysDictDao.h b/dao/SysDictDao.h new file mode 100644 index 0000000..7c73aff --- /dev/null +++ b/dao/SysDictDao.h @@ -0,0 +1,23 @@ +#ifndef SYSDICTDAO_H +#define SYSDICTDAO_H + +#include +#include "BaseDao.h" + +class SysDictDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysDictDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + QVariantMap findChildDictByCode(QString code); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +}; + +#endif // SYSDICTDAO_H diff --git a/dao/SysPersonDao.cpp b/dao/SysPersonDao.cpp new file mode 100644 index 0000000..3e81fc6 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,317 @@ +#include "SysPersonDao.h" + + +SysPersonDao::SysPersonDao(QObject *parent) : BaseDao(parent) +{ + +} + +QVector SysPersonDao::findAllRecord() +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0'"; + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + count++; + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + result.append(item); + } + +// LOG(TRACE) << QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString(); +// LOG_TRACE(QString("查询SYS_PERSON表的所有记录[%1]").arg(count).toStdString()); + + return result; +} + +QVariantMap SysPersonDao::findRecordById(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = QString("SELECT * FROM SYS_PERSON LEFT JOIN SYS_DEPT ON SYS_PERSON.DEPTID = SYS_DEPT.ID WHERE SYS_PERSON.DELFLAG = '0' AND SYS_PERSON.ID = :id"); + + query.prepare(sql); + query.bindValue(":id", id); + + // 执行查询 + query.exec(); + + // 返回结果 + QVariantMap result; + + // 获取结果集的大小 + query.last(); + int count = query.at() + 1; + + if (count >=1) + { + query.first(); + + result.insert("id", query.value("id").toString()); + result.insert("delflag", query.value("delflag").toString()); + result.insert("createtime", query.value("createtime").toString()); + result.insert("updatetime", query.value("updatetime").toString()); + result.insert("name", query.value("name").toString()); + result.insert("gender", query.value("gender").toString()); + result.insert("deptid", query.value("deptid").toString()); + result.insert("id_card_no", query.value("id_card_no").toString()); + result.insert("remarks", query.value("remarks").toString()); + result.insert("person_code", query.value("person_code").toString()); + result.insert("deptname", query.value("fullname").toString()); + result.insert("sync_id", query.value("sync_id").toString()); + result.insert("avatar", query.value("avatar").toString()); + } + +// LOG(TRACE) << QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_TRACE(QString("根据id查询SYS_PERSON表的记录[id=%1][%2]").arg(id).arg(sql).toStdString()); + + return result; +} + +int SysPersonDao::findRecordCountByNameAndDept(QString name, QString dept) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT COUNT(P.ID) AS RECCT FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + // 执行查询 + query.exec(sql); + + // 返回结果 + int result; + + // 遍历查询结果 + if (query.next()) { + result = query.value("RECCT").toInt(); + } + +// LOG(TRACE) << QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门查询SYS_PERSON记录总数[%1][%2]").arg(result).arg(sql).toStdString()); + + return result; +} + +QVector SysPersonDao::findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + // 查询语句 + QString sql = "SELECT * FROM SYS_PERSON P LEFT JOIN SYS_DEPT D ON P.DEPTID = D.ID WHERE P.DELFLAG = '0'"; + if (name.isEmpty() == false) + { + sql += " AND P.NAME LIKE '%" + name + "%'"; + } + if (dept.isEmpty() == false && dept.indexOf("-1") < 0) + { + sql += QString(" AND ((P.DEPTID = '%1') OR (D.PIDS LIKE '%,%1,%'))").arg(dept); + } + + sql += QString(" LIMIT %1 OFFSET %2").arg(limit).arg(offset); + + // 执行查询 + query.exec(sql); + + // 获取结果集的大小 + int count = 0; + + // 返回结果 + QVector result; + + // 遍历查询结果 + while (query.next()) { + QVariantMap item; + + count++; + + item.insert("id", query.value("id").toString()); + item.insert("delflag", query.value("delflag").toString()); + item.insert("createtime", query.value("createtime").toString()); + item.insert("updatetime", query.value("updatetime").toString()); + item.insert("name", query.value("name").toString()); + item.insert("gender", query.value("gender").toString()); + item.insert("deptid", query.value("deptid").toString()); + item.insert("id_card_no", query.value("id_card_no").toString()); + item.insert("remarks", query.value("remarks").toString()); + item.insert("person_code", query.value("person_code").toString()); + item.insert("sync_id", query.value("sync_id").toString()); + item.insert("deptname", query.value("fullname").toString()); + + result.append(item); + } + +// LOG(TRACE) << QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString(); +// LOG_TRACE(QString("根据姓名和部门分页查询SYS_PERSON表的记录[%1][%2]").arg(count).arg(sql).toStdString()); + + return result; +} + +QString SysPersonDao::save(QVariantMap object) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + qulonglong id = ConnectionManager::getInstance()->generateId(); + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // INSERT语句 + QString sql = QString("INSERT INTO SYS_PERSON " + "(ID, DELFLAG, CREATETIME, UPDATETIME, " + "NAME, GENDER, DEPTID, ID_CARD_NO, " + "REMARKS, PERSON_CODE, SYNC_ID, FACE_VALID, IRIS_VALID) " + "VALUES ('%1', '0', '%2', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9', 0, 0)") + .arg(id).arg(tm) + .arg(object.value("name").toString()) + .arg(object.value("gender").toString()) + .arg(object.value("deptid").toString()) + .arg(object.value("id_card_no").toString()) + .arg(object.value("remarks").toString()) + .arg(object.value("person_code").toString()) + .arg(object.value("sync_id").toString()); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行插入 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + + // 返回结果 + if (success == true) + { +// LOG(DEBUG) << QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("保存SYS_PERSON记录成功[ID = %1][%2]").arg(id).arg(sql).toStdString()); + return QString("%1").arg(id); + } + else + { + return "-1"; + } +} + +bool SysPersonDao::edit(QVariantMap newObject, QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = '%1'").arg(tm); + if (newObject.contains("name")) + { + sql.append(QString(", NAME = '%1'").arg(newObject.value("name").toString())); + } + if (newObject.contains("gender")) + { + sql.append(QString(", GENDER = '%1'").arg(newObject.value("gender").toString())); + } + if (newObject.contains("deptid")) + { + sql.append(QString(", DEPTID = '%1'").arg(newObject.value("deptid").toString())); + } + if (newObject.contains("person_code")) + { + sql.append(QString(", PERSON_CODE = '%1'").arg(newObject.value("person_code").toString())); + } + if (newObject.contains("face_valid")) + { + sql.append(QString(", FACE_VALID = '%1'").arg(newObject.value("face_valid").toString())); + } + if (newObject.contains("iris_valid")) + { + sql.append(QString(", IRIS_VALID = '%1'").arg(newObject.value("iris_valid").toString())); + } + + sql.append(QString(" WHERE ID = '%1'").arg(id)); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG(DEBUG) << QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString(); +// LOG_DEBUG(QString("编辑人员[ID=%1][%2]").arg(id).arg(sql).toStdString()); + + // 返回结果 + return success; +} + +bool SysPersonDao::dele(QString id) +{ + // 新建查询 + QSqlQuery query(ConnectionManager::getInstance()->getConnection()); + QSqlQuery irisDel(ConnectionManager::getInstance()->getConnection()); + + QString tm = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + qint64 tmms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + // UPDATE语句 + QString sql = QString("UPDATE SYS_PERSON SET UPDATETIME = :updateTime, DELFLAG = :flag WHERE ID = :id").arg(tm).arg(tmms).arg(id); + query.prepare(sql); + query.bindValue(":id", id); + query.bindValue(":updateTime", tm); + query.bindValue(":flag", tmms); + + // 物理删除人员的人脸和虹膜数据 + QString delIrisSql = QString("DELETE FROM IRIS_DATA WHERE PERSON_ID = :personId"); + irisDel.prepare(delIrisSql); + irisDel.bindValue(":personId", id); + + // 开启事务 + ConnectionManager::getInstance()->getConnection().transaction(); + + // 执行更新 + bool success = query.exec(sql); + query.exec(delIrisSql); + + // 结束事务 + ConnectionManager::getInstance()->getConnection().commit(); + +// LOG_DEBUG(QString("删除人员SYS_PERSON[ID=%1][%2]").arg(id).arg(success).toStdString()); + + // 返回结果 + return success; +} diff --git a/dao/SysPersonDao.h b/dao/SysPersonDao.h new file mode 100644 index 0000000..947cbe6 --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,26 @@ +#ifndef SYSPERSONDAO_H +#define SYSPERSONDAO_H + +#include +#include "BaseDao.h" + +class SysPersonDao : public BaseDao +{ + Q_OBJECT +public: + explicit SysPersonDao(QObject *parent = nullptr); + + QVector findAllRecord(); + QVariantMap findRecordById(QString id); + + int findRecordCountByNameAndDept(QString name, QString dept); + QVector findRecordsByNameAndDept(QString name, QString dept, qint8 limit, qint16 offset); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/dao/dao.pri b/dao/dao.pri new file mode 100644 index 0000000..300225e --- /dev/null +++ b/dao/dao.pri @@ -0,0 +1,20 @@ + +HEADERS += $$PWD/BaseDao.h +HEADERS += $$PWD/IrisDataDao.h +HEADERS += $$PWD/RecognitionRecordsDao.h +HEADERS += $$PWD/SysPersonDao.h +HEADERS += $$PWD/SysDeptDao.h +HEADERS += $$PWD/SysDictDao.h + +SOURCES += $$PWD/BaseDao.cpp +SOURCES += $$PWD/IrisDataDao.cpp +SOURCES += $$PWD/RecognitionRecordsDao.cpp +SOURCES += $$PWD/SysPersonDao.cpp +SOURCES += $$PWD/SysDeptDao.cpp +SOURCES += $$PWD/SysDictDao.cpp + +HEADERS += $$PWD/util/ConnectionManager.h +SOURCES += $$PWD/util/ConnectionManager.cpp +#HEADERS += $$PWD/util/CacheManager.h +#SOURCES += $$PWD/util/CacheManager.cpp + diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..dfbf26d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,54 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#include +#include + +Q_GLOBAL_STATIC(ConnectionManager, cm) + +ConnectionManager::ConnectionManager(QObject *parent) : QObject(parent) +{ + // 初始化数据库连接 + if (QSqlDatabase::contains("qt_sql_dafault_connection")) + { + conn = QSqlDatabase::database("qt_sql_default_connection"); + } + else + { + conn = QSqlDatabase::addDatabase("QSQLITE"); + conn.setDatabaseName(QApplication::applicationDirPath() + "/data/casic.db"); + + bool succ = conn.open(); + if (succ == true) + { +// LOG_INFO(QString("打开数据库操作正常[Open Database Success]").toStdString()); + qDebug() << "打开数据库操作正常[Open Database Success]"; + } else + { +// LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + qDebug() << "打开数据库操作失败[Open Database Failed]"; + } + } +} + +ConnectionManager::~ConnectionManager() +{ + conn.close(); +} + +ConnectionManager* ConnectionManager::getInstance() +{ + return cm; +} + +QSqlDatabase ConnectionManager::getConnection() +{ + return this->conn; +} + +qint64 ConnectionManager::generateId() +{ + return this->idWorker.nextId(); +} diff --git a/dao/util/ConnectionManager.h b/dao/util/ConnectionManager.h new file mode 100644 index 0000000..3121d9a --- /dev/null +++ b/dao/util/ConnectionManager.h @@ -0,0 +1,34 @@ +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include +#include +#include "utils/id/IdWorker.h" +//#include "utils/UtilInclude.h" + +using namespace Jiawa::Core; + +class ConnectionManager : public QObject +{ + Q_OBJECT +public: + explicit ConnectionManager(QObject *parent = nullptr); + ~ConnectionManager(); + + static ConnectionManager * getInstance(); + + QSqlDatabase getConnection(); + qint64 generateId(); + +private: + // 数据库连接 + QSqlDatabase conn; + + // 雪花id生成工具 + IdWorker &idWorker = Singleton::instance(); + +signals: + +}; + +#endif // CONNECTIONMANAGER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0a0916f --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "MainWindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..663db0f --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,43 @@ +#include "SettingConfig.h" +#include + +SettingConfig::SettingConfig() +{ + filename = QApplication::applicationDirPath() + "/conf/config.ini"; + setting = new QSettings(this->filename, QSettings::IniFormat); + + WINDOW_WIDTH = getProperty("window", "width").toInt(); + WINDOW_HEIGHT = getProperty("window", "height").toInt(); + WINDOW_BACKGROUND_COLOR = getProperty("window", "backgroundColor").toString(); + WINDOW_TITLE = getProperty("window", "title").toString(); + WINDOW_RIGHTS = getProperty("window", "copyright").toString(); + WINDOW_VERSION = getProperty("window", "version").toString(); + DEVICE_CODE = getProperty("window", "deviceCode").toString(); + + IRIS_FRAME_WIDTH = getProperty("camera", "irisFrameWidth").toInt(); + IRIS_FRAME_HEIGHT = getProperty("camera", "irisFrameHeight").toInt(); + IRIS_WIDTH = getProperty("camera", "irisWidth").toInt(); + IRIS_HEIGHT = getProperty("camera", "irisHeight").toInt(); + IRIS_FRAME_INTERVAL = getProperty("camera", "irisFrameInterval").toInt(); + IRIS_DISPLAY_WIDTH = getProperty("camera", "irisDisplayWidth").toInt(); + IRIS_DISPLAY_HEIGHT = getProperty("camera", "irisDisplayHeight").toInt(); + LOCK_FRAME_INTERVAL = getProperty("camera", "lockFrameInterval").toInt(); + + MAX_MATCH_TIME = getProperty("recognize", "maxMatchTime").toInt(); + SUCCESS_TIPS_LAST = getProperty("recognize", "successTipsLast").toInt(); + FAILURE_TIPS_LAST = getProperty("recognize", "failureTipsLast").toInt(); + MAX_FACE_TRY_COUNT = getProperty("recognize", "maxFaceTryCount").toInt(); + MAX_FACE_NOT_FOUND_COUNT = getProperty("recognize", "maxFaceNotFoundCount").toInt(); + MAX_IRIS_TRY_COUNT = getProperty("recognize", "maxIrisTryCount").toInt(); + MAX_EYE_NOT_FOUND_COUNT = getProperty("recognize", "maxEyeNotFoundCount").toInt(); + MIN_EYE_SIZE = getProperty("recognize", "minEyeSize").toInt(); + + LOG_FILE = getProperty("log", "logFile").toString(); + LOG_LEVEL = getProperty("log", "logLevel").toString(); +} + + +QVariant SettingConfig::getProperty(QString nodeName, QString keyName) { + QVariant var = this->setting->value(QString("/%1/%2").arg(nodeName).arg(keyName)); + return var; +} diff --git a/utils/SettingConfig.h b/utils/SettingConfig.h new file mode 100644 index 0000000..b65f553 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,65 @@ +#ifndef SETTINGCONFIG_H +#define SETTINGCONFIG_H + +#include +#include + +class SettingConfig : public QObject +{ +public: + ~SettingConfig() {}; + SettingConfig(const SettingConfig&)=delete; + SettingConfig& operator=(const SettingConfig&)=delete; + + static SettingConfig& getInstance() { + static SettingConfig instance; + return instance; + } + + /** + * @brief get + * @param nodeName + * @param keyName + * @return QVariant + * @title + */ + QVariant getProperty(QString nodeName, QString keyName); + + /******** 以下为需要的各类参数 ********/ + int WINDOW_WIDTH; + int WINDOW_HEIGHT; + QString WINDOW_BACKGROUND_COLOR; + QString WINDOW_TITLE; + QString WINDOW_RIGHTS; + QString WINDOW_VERSION; + QString DEVICE_CODE; + + int IRIS_FRAME_INTERVAL; + int IRIS_FRAME_WIDTH; + int IRIS_FRAME_HEIGHT; + int IRIS_WIDTH; + int IRIS_HEIGHT; + int IRIS_DISPLAY_WIDTH; + int IRIS_DISPLAY_HEIGHT; + int LOCK_FRAME_INTERVAL; + + int MAX_MATCH_TIME; + int SUCCESS_TIPS_LAST; + int FAILURE_TIPS_LAST; + int MAX_FACE_TRY_COUNT; + int MAX_FACE_NOT_FOUND_COUNT; + int MAX_IRIS_TRY_COUNT; + int MAX_EYE_NOT_FOUND_COUNT; + int MIN_EYE_SIZE; + + QString LOG_FILE; + QString LOG_LEVEL; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/id/IdWorker.h b/utils/id/IdWorker.h new file mode 100644 index 0000000..6c48aa9 --- /dev/null +++ b/utils/id/IdWorker.h @@ -0,0 +1,218 @@ +#ifndef _JW_CORE_ID_WORKER_H_ +#define _JW_CORE_ID_WORKER_H_ + +#include +#include +#include +#include +#include +#include "Noncopyable.h" +#include "Singleton.h" + +// 如果不使用 mutex, 则开启下面这个定义, 但是我发现, 还是开启 mutex 功能, 速度比较快 +// #define SNOWFLAKE_ID_WORKER_NO_LOCK + +namespace Jiawa { + + /** + * @brief 核心 + * 核心功能 + */ + namespace Core { + + /** + * @brief 分布式id生成类 + * https://segmentfault.com/a/1190000011282426 + * https://github.com/twitter/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala + * + * 64bit id: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 + * || || || | | | + * |└---------------------------时间戳--------------------------┘└中心-┘└机器-┘ └----序列号----┘ + * | + * 不用 + * SnowFlake的优点: 整体上按照时间自增排序, 并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分), 并且效率较高, 经测试, SnowFlake每秒能够产生26万ID左右. + */ + class SnowflakeIdWorker : private Noncopyable { + + // 实现单例 + friend class Singleton; + + public: + typedef unsigned int UInt; + typedef unsigned long long int UInt64; + +#ifdef SNOWFLAKE_ID_WORKER_NO_LOCK + typedef std::atomic AtomicUInt; + typedef std::atomic AtomicUInt64; +#else + typedef UInt AtomicUInt; + typedef UInt64 AtomicUInt64; +#endif + + void setWorkerId(UInt workerId) { + this->workerId = workerId; + } + + void setDatacenterId(UInt datacenterId) { + this->datacenterId = datacenterId; + } + + UInt64 getId() { + return nextId(); + } + + /** + * 获得下一个ID (该方法是线程安全的) + * + * @return SnowflakeId + */ + UInt64 nextId() { +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + std::unique_lock lock{ mutex }; + AtomicUInt64 timestamp{ 0 }; +#else + static AtomicUInt64 timestamp{ 0 }; +#endif + timestamp = timeGen(); + + // 如果当前时间小于上一次ID生成的时间戳, 说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + std::ostringstream s; + s << "clock moved backwards. Refusing to generate id for " << lastTimestamp - timestamp << " milliseconds"; + throw std::exception(std::runtime_error(s.str())); + } + + if (lastTimestamp == timestamp) { + // 如果是同一时间生成的, 则进行毫秒内序列 + sequence = (sequence + 1) & sequenceMask; + if (0 == sequence) { + // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0; + } + +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + lastTimestamp = timestamp; +#else + lastTimestamp = timestamp.load(); +#endif + + // 移位并通过或运算拼到一起组成64位的ID + return ((timestamp - twepoch) << timestampLeftShift) + | (datacenterId << datacenterIdShift) + | (workerId << workerIdShift) + | sequence; + } + + protected: + SnowflakeIdWorker() : workerId(0), datacenterId(0), sequence(0), lastTimestamp(0) { } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + UInt64 timeGen() const { + auto t = std::chrono::time_point_cast(std::chrono::high_resolution_clock::now()); + return t.time_since_epoch().count(); + } + + /** + * 阻塞到下一个毫秒, 直到获得新的时间戳 + * + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + UInt64 tilNextMillis(UInt64 lastTimestamp) const { + UInt64 timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + private: + +#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK + std::mutex mutex; +#endif + + /** + * 开始时间截 (2018-01-01 00:00:00.000) + */ + const UInt64 twepoch = 1514736000000; + + /** + * 机器id所占的位数 + */ + const UInt workerIdBits = 5; + + /** + * 数据中心id所占的位数 + */ + const UInt datacenterIdBits = 5; + + /** + * 序列所占的位数 + */ + const UInt sequenceBits = 12; + + /** + * 机器ID向左移12位 + */ + const UInt workerIdShift = sequenceBits; + + /** + * 数据标识id向左移17位 + */ + const UInt datacenterIdShift = workerIdShift + workerIdBits; + + /** + * 时间截向左移22位 + */ + const UInt timestampLeftShift = datacenterIdShift + datacenterIdBits; + + /** + * 支持的最大机器id, 结果是31 + */ + const UInt maxWorkerId = -1 ^ (-1 << workerIdBits); + + /** + * 支持的最大数据中心id, 结果是31 + */ + const UInt maxDatacenterId = -1 ^ (-1 << datacenterIdBits); + + /** + * 生成序列的掩码, 这里为4095 + */ + const UInt sequenceMask = -1 ^ (-1 << sequenceBits); + + /** + * 工作机器id(0~31) + */ + UInt workerId; + + /** + * 数据中心id(0~31) + */ + UInt datacenterId; + + /** + * 毫秒内序列(0~4095) + */ + AtomicUInt sequence{ 0 }; + + /** + * 上次生成ID的时间截 + */ + AtomicUInt64 lastTimestamp{ 0 }; + + }; + + typedef SnowflakeIdWorker IdWorker; + } +} + +#endif // _JW_CORE_ID_WORKER_H_ diff --git a/utils/id/Noncopyable.h b/utils/id/Noncopyable.h new file mode 100644 index 0000000..d87f58a --- /dev/null +++ b/utils/id/Noncopyable.h @@ -0,0 +1,31 @@ +#ifndef _JW_CORE_NONCOPYABLE_H_ +#define _JW_CORE_NONCOPYABLE_H_ + + +// Private copy constructor and copy assignment ensure classes derived from +// class noncopyable cannot be copied. + +namespace Jiawa { + namespace Core { + + // protection from unintended ADL(Argument Dependent Lookup) + namespace Noncopyable_ { + + class Noncopyable + { + protected: + Noncopyable() = default; + ~Noncopyable() = default; + + Noncopyable(const Noncopyable&) = delete; + Noncopyable(const Noncopyable&&) = delete; + Noncopyable& operator=(const Noncopyable&) = delete; + Noncopyable& operator=(const Noncopyable&&) = delete; + }; + } + + typedef Noncopyable_::Noncopyable Noncopyable; + } +} + +#endif // _JW_CORE_NONCOPYABLE_H_ diff --git a/utils/id/Singleton.h b/utils/id/Singleton.h new file mode 100644 index 0000000..d1c0553 --- /dev/null +++ b/utils/id/Singleton.h @@ -0,0 +1,60 @@ +#ifndef _JW_CORE_SINGLETON_H_ +#define _JW_CORE_SINGLETON_H_ + +namespace Jiawa { + namespace Core { + + // boost/container/detail/singleton.hpp + // http://blog.csdn.net/fullsail/article/details/8483106 + // T must be: no-throw default constructible and no-throw destructible + template + class Singleton { + private: + Singleton() = default; + ~Singleton() = default; + + private: + struct object_creator + { + // This constructor does nothing more than ensure that instance() + // is called before main() begins, thus creating the static + // T object before multithreading race issues can come up. + object_creator() { Singleton::instance(); } + inline void do_nothing() const { } + }; + static object_creator create_object; + + private: + Singleton(const Singleton&) = delete; + Singleton(const Singleton&&) = delete; + Singleton& operator=(const Singleton&) = delete; + Singleton& operator=(const Singleton&&) = delete; + + public: + typedef T object_type; + + // If, at any point (in user code), Singleton::instance() + // is called, then the following function is instantiated. + static object_type& instance() + { + // This is the object that we return a reference to. + // It is guaranteed to be created before main() begins because of + // the next line. + static object_type obj; + + // The following line does nothing else than force the instantiation + // of Singleton::create_object, whose constructor is + // called before main() begins. + create_object.do_nothing(); + + return obj; + } + }; + + template + typename Singleton::object_creator Singleton::create_object; + } +} + +#endif // _JW_CORE_SINGLETON_H_ + diff --git a/utils/id/Timer.h b/utils/id/Timer.h new file mode 100644 index 0000000..0f1ff1b --- /dev/null +++ b/utils/id/Timer.h @@ -0,0 +1,57 @@ +#ifndef _JW_CORE_TIMER_H_ +#define _JW_CORE_TIMER_H_ + +#include + +namespace Jiawa { + + /** + * @brief 核心 + * 核心功能 + */ + namespace Core { + + /** + * @brief 计时器类 + * 计时 + */ + template + class Timer { + public: + + typedef CLOCK clock; + typedef typename CLOCK::rep rep; + typedef typename CLOCK::duration duration; + typedef typename CLOCK::time_point time_point; + + Timer() : m_start(CLOCK::now()) { + } + + /** + * @brief 重置计时器 + * + * @return 开始的时间点 + */ + time_point reset() { + m_start = CLOCK::now(); + return m_start; + } + + /** + * @brief 得到流逝的时间 + * @param UNIT 返回时间的类型 + * + * @return 返回流逝的时间 + */ + template + typename UNIT::duration::rep elapsed() const { + return std::chrono::duration_cast(CLOCK::now() - m_start).count(); + } + + private: + time_point m_start; + }; + } +} + +#endif // _JW_CORE_TIMER_H_ \ No newline at end of file diff --git a/utils/utils.pri b/utils/utils.pri new file mode 100644 index 0000000..327f870 --- /dev/null +++ b/utils/utils.pri @@ -0,0 +1,28 @@ + +#HEADERS += $$PWD/UtilInclude.h + +HEADERS += $$PWD/id/IdWorker.h +HEADERS += $$PWD/id/Noncopyable.h +HEADERS += $$PWD/id/Singleton.h +HEADERS += $$PWD/id/Timer.h + +#HEADERS += $$PWD/ImageUtil.h +#SOURCES += $$PWD/ImageUtil.cpp + +#HEADERS += $$PWD/LogUtil.h + +HEADERS += $$PWD/SettingConfig.h +SOURCES += $$PWD/SettingConfig.cpp + +#HEADERS += $$PWD/TimeCounterUtil.h +#SOURCES += $$PWD/TimeCounterUtil.cpp + +#HEADERS += $$PWD/SpeakerUtil.h +#SOURCES += $$PWD/SpeakerUtil.cpp + +#HEADERS += $$PWD/SocketClientUtil.h +#SOURCES += $$PWD/SocketClientUtil.cpp + +#HEADERS += $$PWD/ByteUtil.h +#SOURCES += $$PWD/ByteUtil.cpp +